From b751fecfa44012654bc83784341c9a411d8619ec Mon Sep 17 00:00:00 2001 From: ix0rai Date: Tue, 7 May 2024 17:07:56 -0500 Subject: [PATCH 01/40] port work --- build.gradle | 5 ++-- gradle.properties | 9 +++---- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 17 +++++++------ gradlew.bat | 20 +++++++-------- .../rainglow/config/CustomModeScreen.java | 15 ++++++----- .../rainglow/config/DeferredSaveOption.java | 7 +++++ .../rainglow/config/RainglowConfigScreen.java | 12 +++------ .../rainglow/config/RainglowScreen.java | 24 ------------------ .../rainglow/mixin/AllayEntityMixin.java | 2 +- .../client/AllayEntityRendererMixin.java | 4 +-- .../mixin/client/SquidInkParticleMixin.java | 2 +- src/main/resources/rainglow.accesswidener | 1 + 14 files changed, 49 insertions(+), 71 deletions(-) create mode 100644 src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java delete mode 100644 src/main/java/io/ix0rai/rainglow/config/RainglowScreen.java diff --git a/build.gradle b/build.gradle index 8b5d349..49583a1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id "fabric-loom" version "1.5.+" - id "com.github.johnrengelman.shadow" version "7.1.2" + id "fabric-loom" version "1.6.+" + id "com.github.johnrengelman.shadow" version "7.1.+" id "com.modrinth.minotaur" version "2.+" } @@ -25,7 +25,6 @@ dependencies { mappings("org.quiltmc:quilt-mappings:${project.minecraft_version}+build.${project.quilt_mappings}:intermediary-v2") modImplementation("net.fabricmc:fabric-loader:${project.loader_version}") - include(modImplementation("dev.lambdaurora:spruceui:${project.spruceui_version}")) modImplementation("com.terraformersmc:modmenu:${project.mod_menu_version}") Set apiModules = [ diff --git a/gradle.properties b/gradle.properties index 22ba06d..a9ba895 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,17 +3,16 @@ org.gradle.jvmargs=-Xmx1G # minecraft, mappings and loader dependencies # check these on https://modmuss50.me/fabric.html -minecraft_version=1.20.4 +minecraft_version=1.20.6 quilt_mappings=3 loader_version=0.15.7 # mod properties -mod_version=1.2.0+mc1.20.4 +mod_version=1.2.0+mc1.20.6 maven_group=rainglow archives_base_name=rainglow # other dependencies -java_version=17 -spruceui_version=5.0.3+1.20.4 +java_version=21 mod_menu_version=9.0.0 -fabric_api_version=0.96.4+1.20.4 +fabric_api_version=0.97.8+1.20.6 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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≫L+crb^Bz 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +145,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 +153,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 @@ -201,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# 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. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_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" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ 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. +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 @@ -57,11 +57,11 @@ 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. +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 diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index b0537a2..9f18df3 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -1,27 +1,28 @@ package io.ix0rai.rainglow.config; -import dev.lambdaurora.spruceui.Position; -import dev.lambdaurora.spruceui.option.SpruceBooleanOption; -import dev.lambdaurora.spruceui.option.SpruceOption; -import dev.lambdaurora.spruceui.option.SpruceSimpleActionOption; -import dev.lambdaurora.spruceui.widget.container.SpruceOptionListWidget; import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class CustomModeScreen extends RainglowScreen { +public class CustomModeScreen extends SimpleOptionsScreen { private final SpruceOption clearOption; private final SpruceOption saveOption; private final SpruceBooleanOption[] colourToggles = new SpruceBooleanOption[RainglowColour.values().length]; private final boolean[] toggleStates = new boolean[RainglowColour.values().length]; public CustomModeScreen(@Nullable Screen parent) { - super(parent, Rainglow.translatableText("config.title")); + super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), + + ); + + // todo subclass option to allow saving via a save button + // ephemeral value, not saved until a specific method is called (will happen on save pressed) // create toggles for each colour for (int i = 0; i < RainglowColour.values().length; i ++) { diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java new file mode 100644 index 0000000..df95d9d --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -0,0 +1,7 @@ +package io.ix0rai.rainglow.config; + +import net.minecraft.client.option.Option; + +public class DeferredSaveOption extends Option { + private final T deferredValue; +} diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 8b2792f..5e2f017 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -1,19 +1,12 @@ package io.ix0rai.rainglow.config; -import dev.lambdaurora.spruceui.Position; -import dev.lambdaurora.spruceui.option.SpruceBooleanOption; -import dev.lambdaurora.spruceui.option.SpruceCyclingOption; -import dev.lambdaurora.spruceui.option.SpruceIntegerInputOption; -import dev.lambdaurora.spruceui.option.SpruceOption; -import dev.lambdaurora.spruceui.option.SpruceSimpleActionOption; -import dev.lambdaurora.spruceui.widget.SpruceLabelWidget; -import dev.lambdaurora.spruceui.widget.container.SpruceOptionListWidget; import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.option.Option; import net.minecraft.client.toast.SystemToast; import net.minecraft.client.toast.Toast; import net.minecraft.text.Style; @@ -39,6 +32,7 @@ public class RainglowConfigScreen extends RainglowScreen { public RainglowConfigScreen(@Nullable Screen parent) { super(parent, Rainglow.translatableText("config.title")); + Option.ofBoolean() this.mode = Rainglow.CONFIG.getMode(); // mode option cycles through available modes @@ -171,7 +165,7 @@ private SpruceLabelWidget createColourListLabel(String translationKey, RainglowM } private static void sendConfigLockedToast() { - Toast toast = new SystemToast(SystemToast.C_ozahoshp.field_47585, Rainglow.translatableText("config.server_locked_title"), Rainglow.translatableText("config.server_locked_description")); + Toast toast = new SystemToast(SystemToast.Id.PACK_LOAD_FAILURE, Rainglow.translatableText("config.server_locked_title"), Rainglow.translatableText("config.server_locked_description")); MinecraftClient.getInstance().getToastManager().add(toast); } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowScreen.java deleted file mode 100644 index e5759a5..0000000 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowScreen.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.ix0rai.rainglow.config; - -import dev.lambdaurora.spruceui.screen.SpruceScreen; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; -import org.jetbrains.annotations.Nullable; - -public abstract class RainglowScreen extends SpruceScreen { - protected final Screen parent; - - protected RainglowScreen(@Nullable Screen parent, Text title) { - super(title); - this.parent = parent; - } - - @Override - public void closeScreen() { - if (this.client != null) { - this.client.setScreen(this.parent); - } else { - super.closeScreen(); - } - } -} diff --git a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java index 2c7ab16..fd58826 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java @@ -26,7 +26,7 @@ protected AllayEntityMixin(EntityType entityType, World w @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(CallbackInfo ci) { - this.getDataTracker().startTracking(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), RainglowColour.BLUE.getId()); + this.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java index b1ae55c..7efc1f6 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java @@ -3,7 +3,7 @@ import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; -import net.minecraft.client.render.entity.AllayRenderer; +import net.minecraft.client.render.entity.AllayEntityRenderer; import net.minecraft.entity.passive.AllayEntity; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(AllayRenderer.class) +@Mixin(AllayEntityRenderer.class) public class AllayEntityRendererMixin { @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java index 11f77e6..797c136 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java @@ -50,7 +50,7 @@ public void createParticle(DefaultParticleType defaultParticleType, ClientWorld rgb = RainglowColour.WHITE.getInkRgb(); } - cir.setReturnValue(new SquidInkParticle(clientWorld, d, e, f, g, h, i, ColorUtil.ARGB32.getArgb(255, (int) rgb.r(), (int) rgb.g(), (int) rgb.b()), this.spriteProvider)); + cir.setReturnValue(new SquidInkParticle(clientWorld, d, e, f, g, h, i, ColorUtil.Argb32.of(255, (int) rgb.r(), (int) rgb.g(), (int) rgb.b()), this.spriteProvider)); } } } diff --git a/src/main/resources/rainglow.accesswidener b/src/main/resources/rainglow.accesswidener index 686ef2c..e19e9f7 100644 --- a/src/main/resources/rainglow.accesswidener +++ b/src/main/resources/rainglow.accesswidener @@ -1,6 +1,7 @@ accessWidener v1 named accessible class net/minecraft/client/particle/GlowParticle$GlowFactory +extendable class net/minecraft/client/option/Option accessible method net/minecraft/client/particle/GlowParticle (Lnet/minecraft/client/world/ClientWorld;DDDDDDLnet/minecraft/client/particle/SpriteProvider;)V accessible field net/minecraft/client/particle/GlowParticle RANDOM Lnet/minecraft/util/random/RandomGenerator; From d73dfc3c98c75d4f3091a50b07f4c68fc879574b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Tue, 7 May 2024 20:37:29 -0500 Subject: [PATCH 02/40] port work --- gradle.properties | 6 +- .../java/io/ix0rai/rainglow/Rainglow.java | 6 +- .../rainglow/client/RainglowClient.java | 79 ++++---- .../rainglow/config/CustomModeScreen.java | 158 ++++++++------- .../rainglow/config/DeferredSaveOption.java | 45 ++++- .../rainglow/config/RainglowConfig.java | 5 + .../rainglow/config/RainglowConfigScreen.java | 185 +++++++----------- .../ix0rai/rainglow/data/RainglowColour.java | 51 +++-- .../ix0rai/rainglow/data/RainglowEntity.java | 24 +++ .../io/ix0rai/rainglow/data/RainglowMode.java | 18 ++ .../rainglow/data/RainglowNetworking.java | 76 +++---- .../rainglow/mixin/GlowSquidEntityMixin.java | 5 +- .../ix0rai/rainglow/mixin/MobEntityMixin.java | 3 +- .../rainglow/mixin/SlimeEntityMixin.java | 5 +- src/main/resources/rainglow.accesswidener | 6 + 15 files changed, 385 insertions(+), 287 deletions(-) diff --git a/gradle.properties b/gradle.properties index a9ba895..54aa476 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ org.gradle.jvmargs=-Xmx1G # minecraft, mappings and loader dependencies # check these on https://modmuss50.me/fabric.html minecraft_version=1.20.6 -quilt_mappings=3 -loader_version=0.15.7 +quilt_mappings=6 +loader_version=0.15.11 # mod properties mod_version=1.2.0+mc1.20.6 @@ -15,4 +15,4 @@ archives_base_name=rainglow # other dependencies java_version=21 mod_menu_version=9.0.0 -fabric_api_version=0.97.8+1.20.6 +fabric_api_version=0.98.0+1.20.6 diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 87862d1..90d9f31 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -4,6 +4,7 @@ import io.ix0rai.rainglow.config.RainglowConfig; import io.ix0rai.rainglow.data.*; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.minecraft.entity.data.DataTracker; @@ -42,10 +43,13 @@ public class Rainglow implements ModInitializer { public void onInitialize() { ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener((RainglowResourceReloader) () -> id("server_mode_data")); + PayloadTypeRegistry.playS2C().register(RainglowNetworking.ConfigSyncPayload.PACKET_ID, RainglowNetworking.ConfigSyncPayload.PACKET_CODEC); + PayloadTypeRegistry.playS2C().register(RainglowNetworking.ModeSyncPayload.PACKET_ID, RainglowNetworking.ModeSyncPayload.PACKET_CODEC); + ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { if (CONFIG.isServerSyncEnabled()) { // send modes to client - RainglowNetworking.sendModeData(handler.player); + RainglowNetworking.syncModes(handler.player); // send config to client RainglowNetworking.syncConfig(handler.player); diff --git a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java index 7d53184..99a3a33 100644 --- a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java +++ b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java @@ -3,7 +3,6 @@ import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowMode; import io.ix0rai.rainglow.data.RainglowResourceReloader; -import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowNetworking; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.EnvType; @@ -11,62 +10,62 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.client.MinecraftClient; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; import java.util.ArrayList; -import java.util.Collection; import java.util.List; @Environment(EnvType.CLIENT) public class RainglowClient implements ClientModInitializer { @Override public void onInitializeClient() { - ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.CONFIG_SYNC_ID, (client, handler, buf, responseSender) -> { - String mode = buf.readString(); - - List colourIds = buf.readList(PacketByteBuf::readString); - List colours = colourIds.stream().map(RainglowColour::get).toList(); - - client.execute(() -> { - // custom must be set before mode so that if the server sends a custom mode it is set correctly - // otherwise the client's custom would be used - Rainglow.CONFIG.setCustom(colours); - Rainglow.CONFIG.setMode(RainglowMode.byId(mode)); + ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ConfigSyncPayload.PACKET_ID, (payload, context) -> { + try (MinecraftClient client = context.client()) { + client.execute(() -> { + // custom must be set before mode so that if the server sends a custom mode it is set correctly + // otherwise the client's custom would be used + Rainglow.CONFIG.setCustom(payload.customMode()); + Rainglow.CONFIG.setMode(RainglowMode.byId(payload.currentMode())); + + for (var entry : payload.enabledMobs().entrySet()) { + Rainglow.CONFIG.setEntityEnabled(entry.getKey(), entry.getValue()); + } - // lock the config from reloading on resource reload - Rainglow.CONFIG.setEditLocked(true); + // lock the config from reloading on resource reload + Rainglow.CONFIG.setEditLocked(true); - // log - Rainglow.LOGGER.info("received config from server: set mode to " + mode + " and custom colours to " + colourIds); - }); + // log + Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); + }); + } }); - ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.MODE_SYNC_ID, (client, handler, buf, responseSender) -> { - Collection modes = RainglowNetworking.readModeData(buf); - - client.execute(() -> { - List newModeIds = new ArrayList<>(); - - // add modes that do not exist on the client to the map - for (RainglowMode mode : modes) { - if (!mode.existsLocally()) { - newModeIds.add(mode.getId()); - RainglowMode.addMode(mode); + ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ModeSyncPayload.PACKET_ID, (payload, context) -> { + try (MinecraftClient client = context.client()) { + client.execute(() -> { + List newModeIds = new ArrayList<>(); + + // add modes that do not exist on the client to the map + for (RainglowMode mode : payload.modes()) { + if (!mode.existsLocally()) { + newModeIds.add(mode.getId()); + RainglowMode.addMode(mode); + } } - } - // now that we have modes, we can load the config - if (Rainglow.CONFIG.isUninitialised()) { - Rainglow.CONFIG.reloadFromFile(); - } + // now that we have modes, we can load the config + if (Rainglow.CONFIG.isUninitialised()) { + Rainglow.CONFIG.reloadFromFile(); + } - // log - if (!newModeIds.isEmpty()) { - Rainglow.LOGGER.info("received new modes from server: " + newModeIds); - } - }); + // log + if (!newModeIds.isEmpty()) { + Rainglow.LOGGER.info("received new modes from server: " + newModeIds); + } + }); + } }); ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 9f18df3..08e1e26 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -5,86 +5,92 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; +import net.minecraft.client.option.GameOptions; +import net.minecraft.client.option.Option; +import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; public class CustomModeScreen extends SimpleOptionsScreen { - private final SpruceOption clearOption; - private final SpruceOption saveOption; - private final SpruceBooleanOption[] colourToggles = new SpruceBooleanOption[RainglowColour.values().length]; - private final boolean[] toggleStates = new boolean[RainglowColour.values().length]; - - public CustomModeScreen(@Nullable Screen parent) { - super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), - - ); - - // todo subclass option to allow saving via a save button - // ephemeral value, not saved until a specific method is called (will happen on save pressed) - - // create toggles for each colour - for (int i = 0; i < RainglowColour.values().length; i ++) { - final RainglowColour colour = RainglowColour.values()[i]; - final int index = i; - - toggleStates[index] = Rainglow.CONFIG.getCustom().contains(colour); - - colourToggles[index] = new SpruceBooleanOption(Rainglow.translatableTextKey("colour." + colour.getId()), - () -> toggleStates[index], - enable -> toggleStates[index] = enable, - null, - true - ); - } - - // toggles all colours to false - this.clearOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.clear"), - btn -> { - for (int i = 0; i < RainglowColour.values().length; i ++) { - toggleStates[i] = false; - } - - MinecraftClient client = MinecraftClient.getInstance(); - this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); - }); - - // writes all the toggled colours to the config and reloads custom mode - this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), - buttonWidget -> { - List newCustom = new ArrayList<>(); - - for (int i = 0; i < RainglowColour.values().length; i ++) { - if (toggleStates[i]) { - newCustom.add(RainglowColour.values()[i]); - } - } - - Rainglow.CONFIG.setCustom(newCustom); - Rainglow.CONFIG.saveCustom(); - this.closeScreen(); - } - ); - } - - @Override - protected void init() { - super.init(); - - // create a list of toggles for each colour - SpruceOptionListWidget options = new SpruceOptionListWidget(Position.of(0, 22), this.width, this.height - (35 + 22)); - for (int i = 0; i < RainglowColour.values().length; i += 2) { - SpruceOption secondToggle = null; - if (i + 1 < RainglowColour.values().length) { - secondToggle = colourToggles[i + 1]; - } - options.addOptionEntry(colourToggles[i], secondToggle); - } - this.addDrawableSelectableElement(options); - - // save and clear buttons - this.addDrawableSelectableElement(this.clearOption.createWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150)); - this.addDrawableSelectableElement(this.saveOption.createWidget(Position.of(this, this.width / 2 - 155 + 160, this.height - 29), 150)); - } + public CustomModeScreen(Screen parent, GameOptions gameOptions, Text title, Option[] options) { + super(parent, gameOptions, title, options); + } +// private final SpruceOption clearOption; +// private final SpruceOption saveOption; +// private final SpruceBooleanOption[] colourToggles = new SpruceBooleanOption[RainglowColour.values().length]; +// private final boolean[] toggleStates = new boolean[RainglowColour.values().length]; +// +// public CustomModeScreen(@Nullable Screen parent) { +// super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), +// +// ); +// +// // todo subclass option to allow saving via a save button +// // ephemeral value, not saved until a specific method is called (will happen on save pressed) +// +// // create toggles for each colour +// for (int i = 0; i < RainglowColour.values().length; i ++) { +// final RainglowColour colour = RainglowColour.values()[i]; +// final int index = i; +// +// toggleStates[index] = Rainglow.CONFIG.getCustom().contains(colour); +// +// colourToggles[index] = new SpruceBooleanOption(Rainglow.translatableTextKey("colour." + colour.getId()), +// () -> toggleStates[index], +// enable -> toggleStates[index] = enable, +// null, +// true +// ); +// } +// +// // toggles all colours to false +// this.clearOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.clear"), +// btn -> { +// for (int i = 0; i < RainglowColour.values().length; i ++) { +// toggleStates[i] = false; +// } +// +// MinecraftClient client = MinecraftClient.getInstance(); +// this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); +// }); +// +// // writes all the toggled colours to the config and reloads custom mode +// this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), +// buttonWidget -> { +// List newCustom = new ArrayList<>(); +// +// for (int i = 0; i < RainglowColour.values().length; i ++) { +// if (toggleStates[i]) { +// newCustom.add(RainglowColour.values()[i]); +// } +// } +// +// Rainglow.CONFIG.setCustom(newCustom); +// Rainglow.CONFIG.saveCustom(); +// this.closeScreen(); +// } +// ); +// } +// +// @Override +// protected void init() { +// super.init(); +// +// // create a list of toggles for each colour +// SpruceOptionListWidget options = new SpruceOptionListWidget(Position.of(0, 22), this.width, this.height - (35 + 22)); +// for (int i = 0; i < RainglowColour.values().length; i += 2) { +// SpruceOption secondToggle = null; +// if (i + 1 < RainglowColour.values().length) { +// secondToggle = colourToggles[i + 1]; +// } +// options.addOptionEntry(colourToggles[i], secondToggle); +// } +// this.addDrawableSelectableElement(options); +// +// // save and clear buttons +// this.addDrawableSelectableElement(this.clearOption.createWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150)); +// this.addDrawableSelectableElement(this.saveOption.createWidget(Position.of(this, this.width / 2 - 155 + 160, this.height - 29), 150)); +// } } diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index df95d9d..42444f2 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -1,7 +1,50 @@ package io.ix0rai.rainglow.config; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.Option; +import net.minecraft.text.Text; + +import java.util.Objects; +import java.util.function.Consumer; public class DeferredSaveOption extends Option { - private final T deferredValue; + private T deferredValue; + + public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback) { + super(key, tooltipSupplier, textGetter, values, defaultValue, updateCallback); + this.deferredValue = this.value; + } + + @Override + public void set(T value) { + T object = (T) this.getValues().validate(value).orElseGet(() -> { + System.out.println("Illegal option value " + value + " for " + this.text); + return this.defaultValue; + }); + if (!MinecraftClient.getInstance().isRunning()) { + this.deferredValue = object; + } else { + if (!Objects.equals(this.value, object)) { + this.deferredValue = object; + this.updateCallback.accept(this.deferredValue); + } + } + } + + public static DeferredSaveOption createBoolean(boolean defaultValue, String key) { + return new DeferredSaveOption<>( + "rainglow.config." + key, + Option.constantTooltip(Text.translatable("rainglow.config.tooltip." + key)), + (text, value) -> GameOptions.getGenericValueText(text, Text.translatable("ramel.config.value." + key, value)), + Option.BOOLEAN_VALUES, + defaultValue, + value -> { + } + ); + } + + public void save() { + this.value = this.deferredValue; + } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index 3e379bb..1838f6f 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -8,6 +8,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; +import javax.swing.text.html.parser.Entity; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; @@ -150,6 +151,10 @@ public void setEntityEnabled(RainglowEntity entity, boolean enabled) { this.entityToggles.put(entity, enabled); } + public Map getEntityToggles() { + return this.entityToggles; + } + public void save(boolean log) { if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER || !this.isEditLocked(MinecraftClient.getInstance())) { ConfigIo.writeString(MODE_KEY, this.mode.getId()); diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 5e2f017..91d97d3 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -6,6 +6,8 @@ import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; +import net.minecraft.client.gui.widget.text.TextWidget; import net.minecraft.client.option.Option; import net.minecraft.client.toast.SystemToast; import net.minecraft.client.toast.Toast; @@ -18,125 +20,90 @@ import java.util.function.Consumer; import java.util.function.Supplier; -public class RainglowConfigScreen extends RainglowScreen { - private final SpruceOption modeOption; - private final SpruceOption customOption; - private final SpruceOption[] entityToggles = new SpruceOption[RainglowEntity.values().length]; - private final SpruceOption resetOption; - private final SpruceOption saveOption; +public class RainglowConfigScreen extends SimpleOptionsScreen { +// private final SpruceOption modeOption; +// private final SpruceOption customOption; +// private final SpruceOption[] entityToggles = new SpruceOption[RainglowEntity.values().length]; +// private final SpruceOption resetOption; +// private final SpruceOption saveOption; - private final SpruceOption colourRarityOption; + //private final SpruceOption colourRarityOption; private RainglowMode mode; // colours to apply is saved in a variable so that it can be removed from the screen when cycling modes - private SpruceLabelWidget coloursToApplyLabel; + // private SpruceLabelWidget coloursToApplyLabel; public RainglowConfigScreen(@Nullable Screen parent) { - super(parent, Rainglow.translatableText("config.title")); - Option.ofBoolean() - this.mode = Rainglow.CONFIG.getMode(); - - // mode option cycles through available modes - // it also updates the label to show which colours will be applied - this.modeOption = new SpruceCyclingOption(Rainglow.translatableTextKey("config.mode"), - amount -> { - if (!Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { - mode = mode.cycle(); - this.remove(this.coloursToApplyLabel); - this.coloursToApplyLabel = createColourListLabel(Rainglow.translatableTextKey("config.colours_to_apply"), this.mode, this.width / 2 - 125, this.height / 4 + 40); - this.addDrawable(this.coloursToApplyLabel); - } else { - sendConfigLockedToast(); - } - }, - option -> option.getDisplayText(mode.getText()), - Rainglow.translatableText("tooltip.mode", - List.of(RainglowMode.values()) - ) - ); - - // opens a screen to toggle which colours are applied in custom mode - this.customOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.custom"), - btn -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this)) - ); - - // toggles whether entities are rainbow - for (int i = 0; i < RainglowEntity.values().length; i ++) { - RainglowEntity entity = RainglowEntity.values()[i]; - - this.entityToggles[i] = createEntityToggle( - entity, - () -> Rainglow.CONFIG.isEntityEnabled(entity), - enabled -> Rainglow.CONFIG.setEntityEnabled(entity, enabled) - ); - } - - this.colourRarityOption = new SpruceIntegerInputOption(Rainglow.translatableTextKey("config.rarity"), - Rainglow.CONFIG::getRarity, - Rainglow.CONFIG::setRarity, - Rainglow.translatableText("tooltip.rarity") - ); - - // resets the config to default values - this.resetOption = SpruceSimpleActionOption.reset(btn -> { - MinecraftClient client = MinecraftClient.getInstance(); - if (!Rainglow.CONFIG.isEditLocked(client)) { - this.mode = RainglowMode.getDefault(); - this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); - } else { - sendConfigLockedToast(); - } - }); - - // saves values to config file - this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), - buttonWidget -> { - this.closeScreen(); - Rainglow.CONFIG.setMode(this.mode); - Rainglow.CONFIG.save(true); + super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), + new Option[]{ + DeferredSaveOption.ofBoolean("gaming", false) } ); - } - - private SpruceBooleanOption createEntityToggle(RainglowEntity entity, Supplier getter, Consumer setter) { - return new SpruceBooleanOption(Rainglow.translatableTextKey("config." + "enable_" + entity.getId()), - getter, - setter, - null - ); - } - - @Override - protected void init() { - super.init(); - - SpruceOptionListWidget optionList = new SpruceOptionListWidget(Position.of(0, 22), this.width, this.height - (35 + 22)); - for (int i = 0; i < RainglowEntity.values().length; i += 2) { - SpruceOption secondToggle = null; - int l = RainglowEntity.values().length; - if (i + 1 < l) { - secondToggle = this.entityToggles[i + 1]; - } - - optionList.addOptionEntry(this.entityToggles[i], secondToggle); - } - - optionList.addOptionEntry(this.modeOption, this.customOption); - optionList.addSingleOptionEntry(this.colourRarityOption); - this.addDrawableSelectableElement(optionList); - - // current colours label and colours to apply label - SpruceLabelWidget currentColoursLabel = createColourListLabel(Rainglow.translatableTextKey("config.current_colours"), Rainglow.CONFIG.getMode(), this.width / 2 - 290, this.height / 4 + 40); - this.addDrawable(currentColoursLabel); - this.coloursToApplyLabel = createColourListLabel(Rainglow.translatableTextKey("config.colours_to_apply"), this.mode, this.width / 2 - 125, this.height / 4 + 40); - this.addDrawable(this.coloursToApplyLabel); + this.mode = Rainglow.CONFIG.getMode(); - // reset and save buttons - this.addDrawableSelectableElement(this.resetOption.createWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150)); - this.addDrawableSelectableElement(this.saveOption.createWidget(Position.of(this, this.width / 2 - 155 + 160, this.height - 29), 150)); +// // mode option cycles through available modes +// // it also updates the label to show which colours will be applied +// this.modeOption = new SpruceCyclingOption(Rainglow.translatableTextKey("config.mode"), +// amount -> { +// if (!Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { +// mode = mode.cycle(); +// this.remove(this.coloursToApplyLabel); +// this.coloursToApplyLabel = createColourListLabel(Rainglow.translatableTextKey("config.colours_to_apply"), this.mode, this.width / 2 - 125, this.height / 4 + 40); +// this.addDrawable(this.coloursToApplyLabel); +// } else { +// sendConfigLockedToast(); +// } +// }, +// option -> option.getDisplayText(mode.getText()), +// Rainglow.translatableText("tooltip.mode", +// List.of(RainglowMode.values()) +// ) +// ); +// +// // opens a screen to toggle which colours are applied in custom mode +// this.customOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.custom"), +// btn -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this)) +// ); +// +// // toggles whether entities are rainbow +// for (int i = 0; i < RainglowEntity.values().length; i ++) { +// RainglowEntity entity = RainglowEntity.values()[i]; +// +// this.entityToggles[i] = createEntityToggle( +// entity, +// () -> Rainglow.CONFIG.isEntityEnabled(entity), +// enabled -> Rainglow.CONFIG.setEntityEnabled(entity, enabled) +// ); +// } +// +// this.colourRarityOption = new SpruceIntegerInputOption(Rainglow.translatableTextKey("config.rarity"), +// Rainglow.CONFIG::getRarity, +// Rainglow.CONFIG::setRarity, +// Rainglow.translatableText("tooltip.rarity") +// ); +// +// // resets the config to default values +// this.resetOption = SpruceSimpleActionOption.reset(btn -> { +// MinecraftClient client = MinecraftClient.getInstance(); +// if (!Rainglow.CONFIG.isEditLocked(client)) { +// this.mode = RainglowMode.getDefault(); +// this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); +// } else { +// sendConfigLockedToast(); +// } +// }); +// +// // saves values to config file +// this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), +// buttonWidget -> { +// this.closeScreen(); +// Rainglow.CONFIG.setMode(this.mode); +// Rainglow.CONFIG.save(true); +// } +// ); } - private SpruceLabelWidget createColourListLabel(String translationKey, RainglowMode mode, int x, int y) { + private TextWidget createColourListLabel(String translationKey, RainglowMode mode, int x, int y) { // creates a label and appends all the colours that will be applied in the given mode StringBuilder text = new StringBuilder(Language.getInstance().get(translationKey)); int maxDisplayedColourCount = 16; @@ -161,7 +128,7 @@ private SpruceLabelWidget createColourListLabel(String translationKey, RainglowM // set colour to the mode's text colour Style style = Style.EMPTY.withColor(mode.getText().getStyle().getColor()); - return new SpruceLabelWidget(Position.of(this, x + 110, y), Text.literal(text.toString()).setStyle(style), 200, true); + return new TextWidget(Text.literal(text.toString()).setStyle(style), MinecraftClient.getInstance().textRenderer); } private static void sendConfigLockedToast() { diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java index 44d7813..15de842 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java @@ -2,42 +2,46 @@ import net.minecraft.item.Item; import net.minecraft.item.Items; +import net.minecraft.network.PacketByteBuf; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.HashMap; public enum RainglowColour { - BLACK(new RGB(0.0F, 0.0F, 0.0F), new RGB(0.0F, 0.0F, 0.0F), new RGB(0, 0, 0), Items.BLACK_DYE), - BLUE(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.BLUE_DYE), - BROWN(new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(149, 59, 35), Items.BROWN_DYE), // todo particles - CYAN(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.CYAN_DYE), // todo particles - GRAY(new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.GRAY_DYE), - GREEN(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.GREEN_DYE), - INDIGO(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 1.0F), new RGB(0, 0, 200), Items.AMETHYST_SHARD), - LIGHT_BLUE(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.LIGHT_BLUE_DYE), // todo particles - LIGHT_GRAY(new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.LIGHT_GRAY_DYE), // todo particles - LIME(new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.LIME_DYE), // todo particles - MAGENTA(new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.MAGENTA_DYE), // todo particles - ORANGE(new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(200, 0, 0), Items.ORANGE_DYE), - PINK(new RGB(0.6F, 0F, 0.5F), new RGB(1.0F, 0.1F, 1.0F), new RGB(200, 0, 0), Items.PINK_DYE), - PURPLE(new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.PURPLE_DYE), - RED(new RGB(1.0F, 1.0F, 0.8F), new RGB(1.0F, 0.4F, 0.4F), new RGB(200, 0, 0), Items.RED_DYE), - WHITE(new RGB(1.0F, 1.0F, 1.0F), new RGB(1.0F, 1.0F, 1.0F), new RGB(200, 200, 200), Items.WHITE_DYE), - YELLOW(new RGB(1.0F, 1.0F, 0.8F), new RGB(1.0F, 1.0F, 0.4F), new RGB(200, 0, 0), Items.YELLOW_DYE); + BLACK("black", new RGB(0.0F, 0.0F, 0.0F), new RGB(0.0F, 0.0F, 0.0F), new RGB(0, 0, 0), Items.BLACK_DYE), + BLUE("blue", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.BLUE_DYE), + BROWN("brown", new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(149, 59, 35), Items.BROWN_DYE), // todo particles + CYAN("cyan", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.CYAN_DYE), // todo particles + GRAY("gray", new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.GRAY_DYE), + GREEN("green", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.GREEN_DYE), + INDIGO("indigo", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 1.0F), new RGB(0, 0, 200), Items.AMETHYST_SHARD), + LIGHT_BLUE("light_blue", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.LIGHT_BLUE_DYE), // todo particles + LIGHT_GRAY("light_gray", new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.LIGHT_GRAY_DYE), // todo particles + LIME("lime", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.LIME_DYE), // todo particles + MAGENTA("magenta", new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.MAGENTA_DYE), // todo particles + ORANGE("orange", new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(200, 0, 0), Items.ORANGE_DYE), + PINK("pink", new RGB(0.6F, 0F, 0.5F), new RGB(1.0F, 0.1F, 1.0F), new RGB(200, 0, 0), Items.PINK_DYE), + PURPLE("purple", new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.PURPLE_DYE), + RED("red", new RGB(1.0F, 1.0F, 0.8F), new RGB(1.0F, 0.4F, 0.4F), new RGB(200, 0, 0), Items.RED_DYE), + WHITE("white", new RGB(1.0F, 1.0F, 1.0F), new RGB(1.0F, 1.0F, 1.0F), new RGB(200, 200, 200), Items.WHITE_DYE), + YELLOW("yellow", new RGB(1.0F, 1.0F, 0.8F), new RGB(1.0F, 1.0F, 0.4F), new RGB(200, 0, 0), Items.YELLOW_DYE); private static final HashMap BY_ID = new HashMap<>(); static { Arrays.stream(values()).forEach(mode -> BY_ID.put(mode.getId(), mode)); } + private final String id; private Identifier texture; private final RGB passiveParticleRgb; private final RGB altPassiveParticleRgb; private final RGB inkRgb; private final Item item; - RainglowColour(RGB passiveParticleRgb, RGB altPassiveParticleRgb, RGB inkRgb, Item item) { + RainglowColour(String id, RGB passiveParticleRgb, RGB altPassiveParticleRgb, RGB inkRgb, Item item) { + this.id = id; this.texture = new Identifier("textures/entity/squid/" + this.getId() + ".png"); this.passiveParticleRgb = passiveParticleRgb; this.altPassiveParticleRgb = altPassiveParticleRgb; @@ -62,7 +66,7 @@ public Identifier getTexture(RainglowEntity entityType) { } public String getId() { - return this.name().toLowerCase(); + return this.id; } public RGB getPassiveParticleRgb() { @@ -86,10 +90,19 @@ public String toString() { return this.getId(); } + @Nullable public static RainglowColour get(String id) { return BY_ID.get(id); } public record RGB(float r, float g, float b) { } + + public static RainglowColour read(PacketByteBuf buf) { + return get(buf.readString()); + } + + public static void write(PacketByteBuf buf, RainglowColour entity) { + buf.writeString(entity.getId()); + } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 85e8445..76039b8 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -1,10 +1,21 @@ package io.ix0rai.rainglow.data; +import net.minecraft.network.PacketByteBuf; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashMap; + public enum RainglowEntity { GLOW_SQUID("glow_squid"), ALLAY("allay"), SLIME("slime"); + private static final HashMap BY_ID = new HashMap<>(); + static { + Arrays.stream(values()).forEach(mode -> BY_ID.put(mode.getId(), mode)); + } + private final String id; RainglowEntity(String id) { @@ -14,4 +25,17 @@ public enum RainglowEntity { public String getId() { return this.id; } + + public static RainglowEntity read(PacketByteBuf buf) { + return get(buf.readString()); + } + + public static void write(PacketByteBuf buf, RainglowEntity entity) { + buf.writeString(entity.id); + } + + @Nullable + public static RainglowEntity get(String id) { + return BY_ID.get(id); + } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java index 6fc5cf8..796ab91 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java @@ -1,8 +1,10 @@ package io.ix0rai.rainglow.data; import io.ix0rai.rainglow.Rainglow; +import net.minecraft.network.PacketByteBuf; import net.minecraft.text.Style; import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; import net.minecraft.text.TextColor; import java.util.ArrayList; @@ -134,6 +136,22 @@ public static void printLoadedModes() { Rainglow.LOGGER.info("Loaded modes: [" + formatted + "]"); } + public static void write(PacketByteBuf buf, RainglowMode mode) { + buf.writeString(mode.getId()); + TextCodecs.UNLIMITED_TEXT_PACKET_CODEC.encode(buf, mode.getText()); + buf.writeString(mode.getText().toString()); + List colourIds = mode.getColours().stream().map(RainglowColour::getId).toList(); + buf.writeCollection(colourIds, PacketByteBuf::writeString); + } + + public static RainglowMode read(PacketByteBuf buf) { + String id = buf.readString(); + Text text = TextCodecs.UNLIMITED_TEXT_PACKET_CODEC.decode(buf); + List colourIds = buf.readList(PacketByteBuf::readString); + + return new RainglowMode(id, colourIds, text, RainglowMode.byId(id) != null); + } + /** * represents modes loaded from json files diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java index 4a62636..ff6513d 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java @@ -4,58 +4,68 @@ import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.packet.payload.CustomPayload; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; import net.minecraft.util.Identifier; import java.util.Collection; import java.util.List; +import java.util.Map; public class RainglowNetworking { - public static final Identifier CONFIG_SYNC_ID = Rainglow.id("config_sync"); - public static final Identifier MODE_SYNC_ID = Rainglow.id("mode_sync"); - public static void syncConfig(ServerPlayerEntity player) { - PacketByteBuf buf = PacketByteBufs.create(); - - // write current mode - buf.writeString(Rainglow.CONFIG.getMode().getId()); - - // write custom mode data - List colourIds = Rainglow.CONFIG.getCustom().stream().map(RainglowColour::getId).toList(); - buf.writeCollection(colourIds, PacketByteBuf::writeString); - // note: client does not need to know if server sync is enabled or not // they already know that it is enabled because they are receiving this packet - - ServerPlayNetworking.send(player, CONFIG_SYNC_ID, buf); + ServerPlayNetworking.send(player, new ConfigSyncPayload(Rainglow.CONFIG.getMode().getId(), Rainglow.CONFIG.getCustom(), Rainglow.CONFIG.getEntityToggles())); } - public static void sendModeData(ServerPlayerEntity player) { - PacketByteBuf buf = PacketByteBufs.create(); + public record ConfigSyncPayload(String currentMode, List customMode, Map enabledMobs) implements CustomPayload { + public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("config_sync")); + public static final PacketCodec PACKET_CODEC = PacketCodec.create(ConfigSyncPayload::write, ConfigSyncPayload::read); - Collection modes = RainglowMode.values(); - buf.writeCollection(modes, RainglowNetworking::writeMode); + public void write(RegistryByteBuf buf) { + buf.writeString(this.currentMode); + buf.writeCollection(this.customMode, RainglowColour::write); + buf.writeMap(this.enabledMobs, RainglowEntity::write, PacketByteBuf::writeBoolean); + } - ServerPlayNetworking.send(player, MODE_SYNC_ID, buf); - } + public static ConfigSyncPayload read(RegistryByteBuf buf) { + return new ConfigSyncPayload( + buf.readString(), + buf.readList(RainglowColour::read), + buf.readMap(RainglowEntity::read, PacketByteBuf::readBoolean) + ); + } - public static Collection readModeData(PacketByteBuf buf) { - return buf.readList(RainglowNetworking::readMode); + @Override + public Id getId() { + return PACKET_ID; + } } - private static void writeMode(PacketByteBuf buf, RainglowMode mode) { - buf.writeString(mode.getId()); - buf.writeText(mode.getText()); - List colourIds = mode.getColours().stream().map(RainglowColour::getId).toList(); - buf.writeCollection(colourIds, PacketByteBuf::writeString); + public static void syncModes(ServerPlayerEntity player) { + ServerPlayNetworking.send(player, new ModeSyncPayload(RainglowMode.values())); } - private static RainglowMode readMode(PacketByteBuf buf) { - String id = buf.readString(); - Text text = buf.readText(); - List colourIds = buf.readList(PacketByteBuf::readString); + public record ModeSyncPayload(Collection modes) implements CustomPayload { + public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("mode_sync")); + public static final PacketCodec PACKET_CODEC = PacketCodec.create(ModeSyncPayload::write, ModeSyncPayload::read); + + public void write(RegistryByteBuf buf) { + buf.writeCollection(this.modes, RainglowMode::write); + } + + public static ModeSyncPayload read(RegistryByteBuf buf) { + return new ModeSyncPayload( + buf.readList(RainglowMode::read) + ); + } - return new RainglowMode(id, colourIds, text, RainglowMode.byId(id) != null); + @Override + public Id getId() { + return PACKET_ID; + } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java index ae90176..f5d7025 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java @@ -18,6 +18,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.entity.data.DataTracker.Builder; @Mixin(GlowSquidEntity.class) public abstract class GlowSquidEntityMixin extends SquidEntity implements GlowSquidVariantProvider { @@ -27,8 +28,8 @@ protected GlowSquidEntityMixin(EntityType entityType, Wor } @Inject(method = "initDataTracker", at = @At("TAIL")) - protected void initDataTracker(CallbackInfo ci) { - this.getDataTracker().startTracking(Rainglow.getTrackedColourData(RainglowEntity.GLOW_SQUID), RainglowColour.BLUE.getId()); + protected void initDataTracker(Builder builder, CallbackInfo ci) { + builder.add(Rainglow.getTrackedColourData(RainglowEntity.GLOW_SQUID), RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java index 678c5f4..40f43a0 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java @@ -21,6 +21,7 @@ import net.minecraft.world.LocalDifficulty; import net.minecraft.world.ServerWorldAccess; import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -35,7 +36,7 @@ protected MobEntityMixin(EntityType entityType, World world @SuppressWarnings("all") @Inject(method = "initialize", at = @At("RETURN"), cancellable = true) - public void initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, EntityData entityData, NbtCompound entityNbt, CallbackInfoReturnable cir) { + public void initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @Nullable EntityData entityData, CallbackInfoReturnable cir) { if ((Object) this instanceof GlowSquidEntity glowSquid) { String colour = Rainglow.generateRandomColourId(this.getRandom()); ((GlowSquidVariantProvider) glowSquid).setVariant(getColourOrDefault(this.random, RainglowColour.BLUE, colour)); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index 695e561..0a32703 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -6,6 +6,7 @@ import io.ix0rai.rainglow.data.SlimeVariantProvider; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleEffect; @@ -33,8 +34,8 @@ protected SlimeEntityMixin(EntityType entityType, World w } @Inject(method = "initDataTracker", at = @At("TAIL")) - protected void initDataTracker(CallbackInfo ci) { - this.getDataTracker().startTracking(Rainglow.getTrackedColourData(RainglowEntity.SLIME), RainglowColour.LIME.getId()); + protected void initDataTracker(DataTracker.Builder builder, CallbackInfo ci) { + builder.add(Rainglow.getTrackedColourData(RainglowEntity.SLIME), RainglowColour.LIME.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) diff --git a/src/main/resources/rainglow.accesswidener b/src/main/resources/rainglow.accesswidener index e19e9f7..654039e 100644 --- a/src/main/resources/rainglow.accesswidener +++ b/src/main/resources/rainglow.accesswidener @@ -1,7 +1,13 @@ accessWidener v1 named accessible class net/minecraft/client/particle/GlowParticle$GlowFactory + extendable class net/minecraft/client/option/Option +accessible class net/minecraft/client/option/Option$ValueSet +accessible field net/minecraft/client/option/Option value Ljava/lang/Object; +accessible field net/minecraft/client/option/Option defaultValue Ljava/lang/Object; +accessible field net/minecraft/client/option/Option updateCallback Ljava/util/function/Consumer; +accessible field net/minecraft/client/option/Option text Lnet/minecraft/text/Text; accessible method net/minecraft/client/particle/GlowParticle (Lnet/minecraft/client/world/ClientWorld;DDDDDDLnet/minecraft/client/particle/SpriteProvider;)V accessible field net/minecraft/client/particle/GlowParticle RANDOM Lnet/minecraft/util/random/RandomGenerator; From 379dbaf9f5b6694b90d2844a3d9ddf1676705a02 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Tue, 7 May 2024 20:47:41 -0500 Subject: [PATCH 03/40] fix dependencies --- build.gradle | 5 ++++- gradle.properties | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 49583a1..22cbaba 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,10 @@ dependencies { Set apiModules = [ "fabric-networking-api-v1", - "fabric-resource-loader-v0" + "fabric-resource-loader-v0", + "fabric-screen-api-v1", + "fabric-key-binding-api-v1", + "fabric-lifecycle-events-v1", ] apiModules.forEach { diff --git a/gradle.properties b/gradle.properties index 54aa476..2524614 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ archives_base_name=rainglow # other dependencies java_version=21 -mod_menu_version=9.0.0 +mod_menu_version=10.0.0-beta.1 fabric_api_version=0.98.0+1.20.6 From 386b2e8156bb4bb3a9333b29ec24599040ddd03b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 8 May 2024 16:30:30 -0500 Subject: [PATCH 04/40] fix networking --- src/main/java/io/ix0rai/rainglow/data/RainglowMode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java index 796ab91..b62cf7b 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java @@ -139,7 +139,6 @@ public static void printLoadedModes() { public static void write(PacketByteBuf buf, RainglowMode mode) { buf.writeString(mode.getId()); TextCodecs.UNLIMITED_TEXT_PACKET_CODEC.encode(buf, mode.getText()); - buf.writeString(mode.getText().toString()); List colourIds = mode.getColours().stream().map(RainglowColour::getId).toList(); buf.writeCollection(colourIds, PacketByteBuf::writeString); } From 5b8f679586224fc6169fcfe65865c9ab1df33622 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 8 May 2024 16:33:19 -0500 Subject: [PATCH 05/40] fix networking so ummm. why is there an autocloseable if i'm not supposed to close it --- .../rainglow/client/RainglowClient.java | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java index 99a3a33..a478f66 100644 --- a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java +++ b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java @@ -22,50 +22,48 @@ public class RainglowClient implements ClientModInitializer { @Override public void onInitializeClient() { ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ConfigSyncPayload.PACKET_ID, (payload, context) -> { - try (MinecraftClient client = context.client()) { - client.execute(() -> { - // custom must be set before mode so that if the server sends a custom mode it is set correctly - // otherwise the client's custom would be used - Rainglow.CONFIG.setCustom(payload.customMode()); - Rainglow.CONFIG.setMode(RainglowMode.byId(payload.currentMode())); + MinecraftClient client = context.client(); + client.execute(() -> { + // custom must be set before mode so that if the server sends a custom mode it is set correctly + // otherwise the client's custom would be used + Rainglow.CONFIG.setCustom(payload.customMode()); + Rainglow.CONFIG.setMode(RainglowMode.byId(payload.currentMode())); - for (var entry : payload.enabledMobs().entrySet()) { - Rainglow.CONFIG.setEntityEnabled(entry.getKey(), entry.getValue()); - } + for (var entry : payload.enabledMobs().entrySet()) { + Rainglow.CONFIG.setEntityEnabled(entry.getKey(), entry.getValue()); + } - // lock the config from reloading on resource reload - Rainglow.CONFIG.setEditLocked(true); + // lock the config from reloading on resource reload + Rainglow.CONFIG.setEditLocked(true); - // log - Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); - }); - } + // log + Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); + }); }); ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ModeSyncPayload.PACKET_ID, (payload, context) -> { - try (MinecraftClient client = context.client()) { - client.execute(() -> { - List newModeIds = new ArrayList<>(); + MinecraftClient client = context.client(); + client.execute(() -> { + List newModeIds = new ArrayList<>(); - // add modes that do not exist on the client to the map - for (RainglowMode mode : payload.modes()) { - if (!mode.existsLocally()) { - newModeIds.add(mode.getId()); - RainglowMode.addMode(mode); - } + // add modes that do not exist on the client to the map + for (RainglowMode mode : payload.modes()) { + if (!mode.existsLocally()) { + newModeIds.add(mode.getId()); + RainglowMode.addMode(mode); } + } - // now that we have modes, we can load the config - if (Rainglow.CONFIG.isUninitialised()) { - Rainglow.CONFIG.reloadFromFile(); - } + // now that we have modes, we can load the config + if (Rainglow.CONFIG.isUninitialised()) { + Rainglow.CONFIG.reloadFromFile(); + } - // log - if (!newModeIds.isEmpty()) { - Rainglow.LOGGER.info("received new modes from server: " + newModeIds); - } - }); - } + // log + if (!newModeIds.isEmpty()) { + Rainglow.LOGGER.info("received new modes from server: " + newModeIds); + } + }); }); ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> From 1d479d95c2bbbde460266e4c840c71698522c5ec Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 8 May 2024 16:45:10 -0500 Subject: [PATCH 06/40] work a little bit on fixing data tracking --- src/main/java/io/ix0rai/rainglow/Rainglow.java | 12 ++++++------ .../ix0rai/rainglow/mixin/GlowSquidEntityMixin.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 90d9f31..2914b8b 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -33,7 +33,7 @@ public class Rainglow implements ModInitializer { private static final Map GLOWSQUID_TEXTURES = new HashMap<>(); private static final Map ALLAY_TEXTURES = new HashMap<>(); private static final Map SLIME_TEXTURES = new HashMap<>(); - private static TrackedData glowSquidColour; + public static final TrackedData glowSquidColour = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); private static TrackedData allayColour; private static TrackedData slimeColour; @@ -150,11 +150,11 @@ public static TrackedData getTrackedColourData(RainglowEntity entityType // we simply ensure it isn't loaded until it's needed, and that fixes the issue if (entityType == RainglowEntity.GLOW_SQUID) { - if (glowSquidColour == null) { - glowSquidColour = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); - } - - return glowSquidColour; +// if (glowSquidColour == null) { +// glowSquidColour = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); +// } +// +// return glowSquidColour; } else if (entityType == RainglowEntity.ALLAY) { if (allayColour == null) { allayColour = DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java index f5d7025..dd4abd5 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java @@ -29,7 +29,7 @@ protected GlowSquidEntityMixin(EntityType entityType, Wor @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(Rainglow.getTrackedColourData(RainglowEntity.GLOW_SQUID), RainglowColour.BLUE.getId()); + builder.add(Rainglow.glowSquidColour, RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) From 4b0861e9ab79767e445f2b648c965f417fe99cb7 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 8 May 2024 17:03:11 -0500 Subject: [PATCH 07/40] fix data trackers --- .../java/io/ix0rai/rainglow/Rainglow.java | 37 ++++--------------- .../rainglow/mixin/AllayEntityMixin.java | 5 ++- .../rainglow/mixin/GlowSquidEntityMixin.java | 4 +- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 2914b8b..2d11222 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -33,9 +33,9 @@ public class Rainglow implements ModInitializer { private static final Map GLOWSQUID_TEXTURES = new HashMap<>(); private static final Map ALLAY_TEXTURES = new HashMap<>(); private static final Map SLIME_TEXTURES = new HashMap<>(); - public static final TrackedData glowSquidColour = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); - private static TrackedData allayColour; - private static TrackedData slimeColour; + public static final TrackedData GLOW_SQUID_COLOUR = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); + public static final TrackedData ALLAY_COLOUR = DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING); + public static final TrackedData SLIME_COLOUR = DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING); public static final String CUSTOM_NBT_KEY = "Colour"; @@ -144,32 +144,11 @@ public static Text translatableText(String key) { } public static TrackedData getTrackedColourData(RainglowEntity entityType) { - // we cannot statically load the tracked data because then it gets registered too early - // it breaks the squids' other tracked data, their dark ticks after being hurt - // this is a workaround to make sure the data is registered at the right time - // we simply ensure it isn't loaded until it's needed, and that fixes the issue - - if (entityType == RainglowEntity.GLOW_SQUID) { -// if (glowSquidColour == null) { -// glowSquidColour = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); -// } -// -// return glowSquidColour; - } else if (entityType == RainglowEntity.ALLAY) { - if (allayColour == null) { - allayColour = DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING); - } - - return allayColour; - } else if (entityType == RainglowEntity.SLIME) { - if (slimeColour == null) { - slimeColour = DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING); - } - - return slimeColour; - } - - throw new RuntimeException("called getTrackedColourData on an unsupported entity type!"); + return switch (entityType) { + case GLOW_SQUID -> GLOW_SQUID_COLOUR; + case ALLAY -> ALLAY_COLOUR; + case SLIME -> SLIME_COLOUR; + }; } public static String getColour(RainglowEntity entityType, DataTracker tracker, RandomGenerator random) { diff --git a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java index fd58826..f5a8374 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java @@ -6,6 +6,7 @@ import io.ix0rai.rainglow.data.RainglowEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.data.DataTracker.Builder; import net.minecraft.entity.passive.AllayEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.random.RandomGenerator; @@ -25,8 +26,8 @@ protected AllayEntityMixin(EntityType entityType, World w } @Inject(method = "initDataTracker", at = @At("TAIL")) - protected void initDataTracker(CallbackInfo ci) { - this.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), RainglowColour.BLUE.getId()); + protected void initDataTracker(Builder builder, CallbackInfo ci) { + builder.add(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java index dd4abd5..83f3139 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java @@ -8,6 +8,7 @@ import net.minecraft.entity.mob.WaterCreatureEntity; import net.minecraft.entity.passive.GlowSquidEntity; import net.minecraft.entity.passive.SquidEntity; +import net.minecraft.entity.data.DataTracker.Builder; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; @@ -18,7 +19,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import net.minecraft.entity.data.DataTracker.Builder; @Mixin(GlowSquidEntity.class) public abstract class GlowSquidEntityMixin extends SquidEntity implements GlowSquidVariantProvider { @@ -29,7 +29,7 @@ protected GlowSquidEntityMixin(EntityType entityType, Wor @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(Rainglow.glowSquidColour, RainglowColour.BLUE.getId()); + builder.add(Rainglow.GLOW_SQUID_COLOUR, RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) From c9d8e57d87063e0d9bbec9cd5f421fa414b80bde Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 8 May 2024 18:00:54 -0500 Subject: [PATCH 08/40] config screen work --- .../java/io/ix0rai/rainglow/Rainglow.java | 2 +- .../rainglow/config/DeferredSaveOption.java | 43 ++++++++++-- .../rainglow/config/RainglowConfig.java | 33 +++++---- .../rainglow/config/RainglowConfigScreen.java | 68 +++++++++++++++++-- .../ix0rai/rainglow/mixin/MobEntityMixin.java | 20 +++++- .../resources/assets/rainglow/lang/en_us.json | 8 ++- 6 files changed, 144 insertions(+), 30 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 2d11222..7ec7846 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -131,7 +131,7 @@ public static boolean colourUnloaded(String colour) { } public static String translatableTextKey(String key) { - if (key.split("\\.").length != 2) throw new IllegalArgumentException("key must be in format \"category.key\""); + if (key.split("\\.").length < 2) throw new IllegalArgumentException("key must be in format \"category.key\": " + key); return MOD_ID + "." + key; } diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 42444f2..7ef3b60 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -1,8 +1,11 @@ package io.ix0rai.rainglow.config; +import com.mojang.serialization.Codec; +import io.ix0rai.rainglow.Rainglow; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.Option; +import net.minecraft.text.CommonTexts; import net.minecraft.text.Text; import java.util.Objects; @@ -12,7 +15,11 @@ public class DeferredSaveOption extends Option { private T deferredValue; public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback) { - super(key, tooltipSupplier, textGetter, values, defaultValue, updateCallback); + this(key, tooltipSupplier, textGetter, values, values.codec(), defaultValue, updateCallback); + } + + public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, Codec codec, T defaultValue, Consumer updateCallback) { + super(key, tooltipSupplier, textGetter, values, codec, defaultValue, updateCallback); this.deferredValue = this.value; } @@ -27,24 +34,48 @@ public void set(T value) { } else { if (!Objects.equals(this.value, object)) { this.deferredValue = object; - this.updateCallback.accept(this.deferredValue); + // note: callback is called on save } } } - public static DeferredSaveOption createBoolean(boolean defaultValue, String key) { + public static Option createDeferredBoolean(String key, boolean defaultValue, Consumer updateCallback) { return new DeferredSaveOption<>( "rainglow.config." + key, Option.constantTooltip(Text.translatable("rainglow.config.tooltip." + key)), - (text, value) -> GameOptions.getGenericValueText(text, Text.translatable("ramel.config.value." + key, value)), + (text, value) -> value ? CommonTexts.YES : CommonTexts.NO, Option.BOOLEAN_VALUES, defaultValue, - value -> { - } + updateCallback + ); + } + + public static Option createDeferredRangedInt(String key, int defaultValue, int min, int max, Consumer updateCallback) { + return new DeferredSaveOption<>( + Rainglow.translatableTextKey("config." + key), + Option.constantTooltip(Rainglow.translatableText("tooltip." + key)), + (text, value) -> Rainglow.translatableText("value." + key, value), + new Option.IntRangeValueSet(min, max), + Codec.intRange(min, max), + defaultValue, + updateCallback + ); + } + + public static Option createDeferredRangedDouble(String key, double defaultValue, double min, double max, Consumer updateCallback) { + return new DeferredSaveOption<>( + "rainglow.config." + key, + Option.constantTooltip(Text.translatable("rainglow.tooltip." + key)), + (text, value) -> GameOptions.getGenericValueText(text, Text.translatable("rainglow.value." + key, value)), + new Option.IntRangeValueSet((int) (min * 10), (int) (max * 10)).withModifier(i -> (double) i / 10.0, double_ -> (int) (double_ * 10.0)), + Codec.doubleRange(min, max), + defaultValue, + updateCallback ); } public void save() { this.value = this.deferredValue; + this.updateCallback.accept(this.value); } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index 1838f6f..8f81cdc 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -8,7 +8,6 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; -import javax.swing.text.html.parser.Entity; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; @@ -20,16 +19,16 @@ public class RainglowConfig { public static final String CUSTOM_KEY = "custom"; public static final String SERVER_SYNC_KEY = "enable_server_sync"; - public static final String RARITY_KEY = "rarity"; public static final Function TO_CONFIG_KEY = entity -> "enable_" + entity.getId(); + public static final Function RARITY_CONFIG_KEY = entity -> entity.getId() + "_rarity"; private RainglowMode mode; private List custom; - private int rarity; private boolean enableServerSync; private boolean editLocked = false; private boolean isInitialised = false; private final Map entityToggles = new EnumMap<>(RainglowEntity.class); + private final Map entityRarities = new EnumMap<>(RainglowEntity.class); public RainglowConfig() { // we cannot load the config here because it would be loaded before modes, since it's statically initialised @@ -80,9 +79,14 @@ public void reloadFromFile() { } // parse rarity - int rarity = 100; - if (config.containsKey(RARITY_KEY)) { - rarity = ConfigIo.parseTomlInt(config.get(RARITY_KEY)); + for (RainglowEntity entity : RainglowEntity.values()) { + String configKey = RARITY_CONFIG_KEY.apply(entity); + + if (config.containsKey(configKey)) { + entityRarities.put(entity, ConfigIo.parseTomlInt(config.get(configKey))); + } else { + entityRarities.put(entity, 100); + } } // reset colours if parsing failed @@ -94,7 +98,6 @@ public void reloadFromFile() { this.mode = rainglowMode; this.custom = customColours; this.enableServerSync = serverSync; - this.rarity = rarity; this.save(false); this.isInitialised = true; @@ -108,8 +111,8 @@ public List getCustom() { return this.custom; } - public int getRarity() { - return this.rarity; + public int getRarity(RainglowEntity entity) { + return this.entityRarities.get(entity); } public boolean isServerSyncEnabled() { @@ -135,8 +138,8 @@ public void setCustom(List custom) { Rainglow.refreshColours(); } - public void setRarity(int rarity) { - this.rarity = rarity; + public void setRarity(RainglowEntity entity, int rarity) { + this.entityRarities.put(entity, rarity); } public void setEditLocked(boolean editLocked) { @@ -160,7 +163,7 @@ public void save(boolean log) { ConfigIo.writeString(MODE_KEY, this.mode.getId()); this.saveCustom(); ConfigIo.writeBoolean(SERVER_SYNC_KEY, this.enableServerSync); - ConfigIo.writeInt(RARITY_KEY, this.rarity); + writeEntityRarities(); } // entity toggles cannot be locked by the server @@ -179,4 +182,10 @@ private void writeEntityToggles() { ConfigIo.writeBoolean(TO_CONFIG_KEY.apply(entry.getKey()), entry.getValue()); } } + + private void writeEntityRarities() { + for (Map.Entry entry : entityRarities.entrySet()) { + ConfigIo.writeInt(RARITY_CONFIG_KEY.apply(entry.getKey()), entry.getValue()); + } + } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 91d97d3..d8d5a76 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -7,39 +7,45 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; +import net.minecraft.client.gui.widget.button.ButtonWidget; +import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; import net.minecraft.client.gui.widget.text.TextWidget; import net.minecraft.client.option.Option; import net.minecraft.client.toast.SystemToast; import net.minecraft.client.toast.Toast; +import net.minecraft.text.CommonTexts; import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Language; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; public class RainglowConfigScreen extends SimpleOptionsScreen { // private final SpruceOption modeOption; // private final SpruceOption customOption; -// private final SpruceOption[] entityToggles = new SpruceOption[RainglowEntity.values().length]; // private final SpruceOption resetOption; // private final SpruceOption saveOption; //private final SpruceOption colourRarityOption; - private RainglowMode mode; + //private RainglowMode mode; // colours to apply is saved in a variable so that it can be removed from the screen when cycling modes // private SpruceLabelWidget coloursToApplyLabel; public RainglowConfigScreen(@Nullable Screen parent) { super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), new Option[]{ - DeferredSaveOption.ofBoolean("gaming", false) + createEntityToggles().get(0), + createColourRaritySliders().get(0), + createEntityToggles().get(1), + createColourRaritySliders().get(1), + createEntityToggles().get(2), + createColourRaritySliders().get(2), } ); - this.mode = Rainglow.CONFIG.getMode(); + // // mode option cycles through available modes // // it also updates the label to show which colours will be applied @@ -103,6 +109,56 @@ public RainglowConfigScreen(@Nullable Screen parent) { // ); } + private static List> createEntityToggles() { + List> toggles = new ArrayList<>(); + + for (RainglowEntity entity : RainglowEntity.values()) { + toggles.add(DeferredSaveOption.createDeferredBoolean( + "enable_" + entity.getId(), + Rainglow.CONFIG.isEntityEnabled(entity), + enabled -> Rainglow.CONFIG.setEntityEnabled(entity, enabled) + )); + } + + return toggles; + } + + private static List> createColourRaritySliders() { + List> sliders = new ArrayList<>(); + + for (RainglowEntity entity : RainglowEntity.values()) { + sliders.add(DeferredSaveOption.createDeferredRangedInt( + entity.getId() + "_rarity", + Rainglow.CONFIG.getRarity(entity), + 0, + 100, + rarity -> Rainglow.CONFIG.setRarity(entity, rarity) + )); + } + + return sliders; + } + + private void save() { + for (Option option : this.options) { + if (option instanceof DeferredSaveOption) { + ((DeferredSaveOption) option).save(); + } + } + } + + @Override + protected void method_31387() { + LinearLayoutWidget linearLayout = this.field_49503.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); + linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); + linearLayout.add(ButtonWidget.builder(CommonTexts.YES, button -> { + this.save(); + this.closeScreen(); + }).build()); + this.field_49503.visitWidgets(this::addDrawableSelectableElement); + this.repositionElements(); + } + private TextWidget createColourListLabel(String translationKey, RainglowMode mode, int x, int y) { // creates a label and appends all the colours that will be applied in the given mode StringBuilder text = new StringBuilder(Language.getInstance().get(translationKey)); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java index 40f43a0..6ab5ea5 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java @@ -6,6 +6,7 @@ import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.GlowSquidEntityData; import io.ix0rai.rainglow.data.GlowSquidVariantProvider; +import io.ix0rai.rainglow.data.RainglowEntity; import io.ix0rai.rainglow.data.SlimeEntityData; import io.ix0rai.rainglow.data.SlimeVariantProvider; import net.minecraft.entity.EntityData; @@ -16,7 +17,6 @@ import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.entity.passive.AllayEntity; import net.minecraft.entity.passive.GlowSquidEntity; -import net.minecraft.nbt.NbtCompound; import net.minecraft.util.random.RandomGenerator; import net.minecraft.world.LocalDifficulty; import net.minecraft.world.ServerWorldAccess; @@ -53,7 +53,21 @@ public void initialize(ServerWorldAccess world, LocalDifficulty difficulty, Spaw } @Unique - private static RainglowColour getColourOrDefault(RandomGenerator random, RainglowColour defaultColour, String randomColour) { - return random.nextInt(100) >= Rainglow.CONFIG.getRarity() ? defaultColour : RainglowColour.get(randomColour); + private RainglowColour getColourOrDefault(RandomGenerator random, RainglowColour defaultColour, String randomColour) { + return random.nextInt(100) >= Rainglow.CONFIG.getRarity(this.getCurrentEntity()) ? defaultColour : RainglowColour.get(randomColour); + } + + @Unique + @SuppressWarnings("all") + private RainglowEntity getCurrentEntity() { + if ((Object) this instanceof GlowSquidEntity) { + return RainglowEntity.GLOW_SQUID; + } else if ((Object) this instanceof AllayEntity) { + return RainglowEntity.ALLAY; + } else if ((Object) this instanceof SlimeEntity) { + return RainglowEntity.SLIME; + } else { + throw new RuntimeException("unsupported entity"); + } } } diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index d209973..3a7f2ca 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -12,8 +12,12 @@ "rainglow.config.enable_allay": "Rainbow allays", "rainglow.config.server_locked_title": "Config is locked by server", "rainglow.config.server_locked_description": "You can't change the mode while the config is locked!", - "rainglow.config.rarity": "Colour Rarity", - "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour", + "rainglow.value.slime_rarity": "Slime rarity: %s", + "rainglow.tooltip.slime_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", + "rainglow.value.allay_rarity": "Allay rarity: %s", + "rainglow.tooltip.allay_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", + "rainglow.value.glow_squid_rarity": "Glow squid rarity: %s", + "rainglow.tooltip.glow_squid_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.tooltip.mode": "Rainglow Mode dictates which colours are currently available for squids", "rainglow.config.cannot_be_loaded_outside_world": "Rainglow config cannot be edited through the GUI before loading a world!", "rainglow.mode.rainbow": "Rainbow", From e28411e1fc815b0fdfcb93ee0ab397cb556bb124 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 12 May 2024 20:07:17 -0500 Subject: [PATCH 09/40] quilt config --- build.gradle | 3 + .../java/io/ix0rai/rainglow/Rainglow.java | 13 +- .../rainglow/client/RainglowClient.java | 16 +- .../io/ix0rai/rainglow/config/ConfigIo.java | 152 ------------- .../rainglow/config/DeferredSaveOption.java | 3 +- .../rainglow/config/RainglowConfig.java | 204 +++++------------- .../rainglow/config/RainglowConfigScreen.java | 12 +- .../io/ix0rai/rainglow/data/RainglowMode.java | 8 +- .../rainglow/data/RainglowNetworking.java | 4 +- .../data/RainglowResourceReloader.java | 3 +- .../client/SlimeEntityRendererMixin.java | 2 +- 11 files changed, 96 insertions(+), 324 deletions(-) delete mode 100644 src/main/java/io/ix0rai/rainglow/config/ConfigIo.java diff --git a/build.gradle b/build.gradle index 22cbaba..4c0a94e 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ repositories { maven { url = "https://maven.gegy.dev/" } maven { url = "https://maven.terraformersmc.com/releases/" } maven { url = "https://maven.quiltmc.org/repository/release/" } + maven { url = "https://repo.sleeping.town/" } } configurations { @@ -26,6 +27,8 @@ dependencies { modImplementation("net.fabricmc:fabric-loader:${project.loader_version}") modImplementation("com.terraformersmc:modmenu:${project.mod_menu_version}") + implementation("folk.sisby:kaleido-config:0.3.0+1.3.0") + include("folk.sisby:kaleido-config:0.3.0+1.3.0") Set apiModules = [ "fabric-networking-api-v1", diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 7ec7846..1efb13b 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -1,12 +1,15 @@ package io.ix0rai.rainglow; import com.google.gson.Gson; +import folk.sisby.kaleido.lib.quiltconfig.api.serializers.TomlSerializer; +import folk.sisby.kaleido.lib.quiltconfig.implementor_api.ConfigEnvironment; import io.ix0rai.rainglow.config.RainglowConfig; import io.ix0rai.rainglow.data.*; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -26,7 +29,9 @@ public class Rainglow implements ModInitializer { public static final String MOD_ID = "rainglow"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); - public static final RainglowConfig CONFIG = new RainglowConfig(); + private static final String FORMAT = "toml"; + private static final ConfigEnvironment ENVIRONMENT = new ConfigEnvironment(FabricLoader.getInstance().getConfigDir(), FORMAT, TomlSerializer.INSTANCE); + public static final RainglowConfig CONFIG = RainglowConfig.create(ENVIRONMENT, MOD_ID, MOD_ID, RainglowConfig.class); public static final Gson GSON = new Gson(); private static final List COLOURS = new ArrayList<>(); @@ -47,7 +52,7 @@ public void onInitialize() { PayloadTypeRegistry.playS2C().register(RainglowNetworking.ModeSyncPayload.PACKET_ID, RainglowNetworking.ModeSyncPayload.PACKET_CODEC); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { - if (CONFIG.isServerSyncEnabled()) { + if (CONFIG.serverSync.value()) { // send modes to client RainglowNetworking.syncModes(handler.player); @@ -72,13 +77,15 @@ public static void setMode(RainglowMode mode) { LOGGER.info("No colours were present in the internal collection, adding blue so that the game doesn't crash"); colours.add(RainglowColour.BLUE); } + colours.forEach(Rainglow::addColour); + CONFIG.setInitialized(); } public static void refreshColours() { // we only ever need to refresh the colours of custom mode, all other sets of colours are immutable if (CONFIG.getMode().getId().equals("custom")) { - setMode(RainglowMode.byId("custom")); + setMode(RainglowMode.get("custom")); } } diff --git a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java index a478f66..985ce8c 100644 --- a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java +++ b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java @@ -1,6 +1,9 @@ package io.ix0rai.rainglow.client; +import folk.sisby.kaleido.lib.quiltconfig.api.values.TrackedValue; +import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueList; import io.ix0rai.rainglow.Rainglow; +import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowMode; import io.ix0rai.rainglow.data.RainglowResourceReloader; import io.ix0rai.rainglow.data.RainglowNetworking; @@ -26,8 +29,11 @@ public void onInitializeClient() { client.execute(() -> { // custom must be set before mode so that if the server sends a custom mode it is set correctly // otherwise the client's custom would be used - Rainglow.CONFIG.setCustom(payload.customMode()); - Rainglow.CONFIG.setMode(RainglowMode.byId(payload.currentMode())); + ValueList list = ValueList.create("", payload.customMode().stream().map(RainglowColour::getId).toArray(String[]::new)); + Rainglow.CONFIG.customColours.setOverride(list); + Rainglow.CONFIG.mode.setOverride(payload.currentMode()); + + // todo override toggles for (var entry : payload.enabledMobs().entrySet()) { Rainglow.CONFIG.setEntityEnabled(entry.getKey(), entry.getValue()); @@ -55,8 +61,8 @@ public void onInitializeClient() { } // now that we have modes, we can load the config - if (Rainglow.CONFIG.isUninitialised()) { - Rainglow.CONFIG.reloadFromFile(); + if (!Rainglow.CONFIG.isInitialized()) { + Rainglow.setMode(Rainglow.CONFIG.getMode()); } // log @@ -73,7 +79,7 @@ public void onInitializeClient() { Rainglow.CONFIG.setEditLocked(false); // reset values to those configured in file - Rainglow.CONFIG.reloadFromFile(); + Rainglow.CONFIG.values().forEach(TrackedValue::removeOverride); } }) ); diff --git a/src/main/java/io/ix0rai/rainglow/config/ConfigIo.java b/src/main/java/io/ix0rai/rainglow/config/ConfigIo.java deleted file mode 100644 index e3f33f2..0000000 --- a/src/main/java/io/ix0rai/rainglow/config/ConfigIo.java +++ /dev/null @@ -1,152 +0,0 @@ -package io.ix0rai.rainglow.config; - -import io.ix0rai.rainglow.Rainglow; -import net.fabricmc.loader.api.FabricLoader; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ConfigIo { - private static final String CONFIG_FILE_NAME = "rainglow.toml"; - private static final Path CONFIG_FILE_PATH = Paths.get(FabricLoader.getInstance().getConfigDir().resolve(CONFIG_FILE_NAME).toUri()); - - private ConfigIo() { - } - - public static boolean parseTomlBoolean(String value) { - return value.equals("true"); - } - - public static int parseTomlInt(String value) { - return Integer.parseInt(value); - } - - public static String parseTomlString(String string) { - try { - return string.split("\"")[1].split("\"")[0]; - } catch (Exception e) { - Rainglow.LOGGER.warn("failed to parse toml string " + string + "; config will reset to default value"); - return ""; - } - } - - public static List parseTomlStringList(String list) { - List parsedList = new ArrayList<>(); - - // trim brackets - try { - String rawList = list.split("\\[")[1].split("]")[0]; - // separate by comma - String[] contents = rawList.split(","); - - for (String item : contents) { - // trim - item = item.trim(); - - // trim quotes and add to list - parsedList.add(item.split("\"")[1].split("\"")[0]); - } - } catch (Exception e) { - Rainglow.LOGGER.error("failed to parse toml list " + list + "; config will reset to default value"); - } - - return parsedList; - } - - public static Map readConfig() { - String content; - try { - content = Files.readString(CONFIG_FILE_PATH); - } catch (IOException e) { - Rainglow.LOGGER.warn("config file not found or corrupted; failed to read: creating new file with default values!"); - createConfigFile(); - return new HashMap<>(); - } - - String[] lines; - try { - lines = content.split("\n"); - } catch (Exception e) { - Rainglow.LOGGER.warn("config file not found or corrupted; failed to read: creating new file with default values!"); - createConfigFile(); - return new HashMap<>(); - } - - Map configData = new HashMap<>(); - - for (String line : lines) { - try { - if (line.isBlank() || line.startsWith("#")) { - continue; - } - - String[] splitLine = line.split("="); - - configData.put(splitLine[0].trim(), splitLine[1].trim()); - } catch (Exception e) { - Rainglow.LOGGER.warn("failed to read line \"" + line + "\" of config file; line will reset to default value"); - } - } - - return configData; - } - - public static void createConfigFile() { - try { - Files.createFile(CONFIG_FILE_PATH); - } catch (IOException e) { - Rainglow.LOGGER.warn("could not create config file!"); - } - } - - public static void writeString(String key, String string) { - write(key, "\"" + string + "\"", "string"); - } - - public static void writeBoolean(String key, boolean bool) { - write(key, bool ? "true" : "false", "boolean"); - } - - public static void writeInt(String key, int value) { - write(key, Integer.toString(value), "int"); - } - - public static void writeStringList(String key, List list) { - // convert to toml-friendly format - StringBuilder tomlCompatibleList = new StringBuilder("["); - for (int i = 0; i < list.size(); i ++) { - tomlCompatibleList.append("\"").append(list.get(i).toString()).append("\"").append(i == list.size() - 1 ? "" : ", "); - } - tomlCompatibleList.append("]"); - - write(key, tomlCompatibleList.toString(), "string list"); - } - - private static void write(String key, String value, String type) { - try { - String content = Files.readString(CONFIG_FILE_PATH); - String[] lines = content.split("\n"); - - for (int i = 0; i < lines.length; i++) { - if (lines[i].startsWith(key)) { - // if key is found replace line - lines[i] = key + " = " + value; - break; - } else if (i == lines.length - 1) { - // if key is not found append it to the end - lines[i] += (!lines[i].isBlank() ? "\n" : "") + key + " = " + value; - } - } - - Files.writeString(CONFIG_FILE_PATH, String.join("\n", lines)); - } catch (IOException e) { - Rainglow.LOGGER.warn("could not write object " + value + " of type " + type + " to config file under key \"" + key + "\"!"); - } - } -} diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 7ef3b60..d610d73 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -25,10 +25,11 @@ public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, Option @Override public void set(T value) { - T object = (T) this.getValues().validate(value).orElseGet(() -> { + T object = this.getValues().validate(value).orElseGet(() -> { System.out.println("Illegal option value " + value + " for " + this.text); return this.defaultValue; }); + if (!MinecraftClient.getInstance().isRunning()) { this.deferredValue = object; } else { diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index 8f81cdc..737863b 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -1,122 +1,71 @@ package io.ix0rai.rainglow.config; -import io.ix0rai.rainglow.Rainglow; +import folk.sisby.kaleido.api.ReflectiveConfig; +import folk.sisby.kaleido.lib.quiltconfig.api.annotations.Comment; +import folk.sisby.kaleido.lib.quiltconfig.api.annotations.SerializedNameConvention; +import folk.sisby.kaleido.lib.quiltconfig.api.metadata.NamingSchemes; +import folk.sisby.kaleido.lib.quiltconfig.api.values.TrackedValue; +import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueList; +import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueMap; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import io.ix0rai.rainglow.data.RainglowMode; -import net.fabricmc.api.EnvType; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; -import java.util.ArrayList; -import java.util.EnumMap; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; - -public class RainglowConfig { - public static final String MODE_KEY = "mode"; - public static final String CUSTOM_KEY = "custom"; - public static final String SERVER_SYNC_KEY = "enable_server_sync"; - - public static final Function TO_CONFIG_KEY = entity -> "enable_" + entity.getId(); - public static final Function RARITY_CONFIG_KEY = entity -> entity.getId() + "_rarity"; - - private RainglowMode mode; - private List custom; - private boolean enableServerSync; - private boolean editLocked = false; - private boolean isInitialised = false; - private final Map entityToggles = new EnumMap<>(RainglowEntity.class); - private final Map entityRarities = new EnumMap<>(RainglowEntity.class); - - public RainglowConfig() { - // we cannot load the config here because it would be loaded before modes, since it's statically initialised - } - - public void reloadFromFile() { - // read config from file - Map config = ConfigIo.readConfig(); - - // parse mode - RainglowMode rainglowMode = RainglowMode.getDefault(); - if (config.containsKey(MODE_KEY)) { - RainglowMode parsedMode = RainglowMode.byId(ConfigIo.parseTomlString(config.get(MODE_KEY))); - if (parsedMode != null) { - rainglowMode = parsedMode; - } - } - - // parse colours for custom mode - // note: we cannot get the default colours from the enum to start off as it's an immutable list - List customColours = new ArrayList<>(); - if (config.containsKey(CUSTOM_KEY)) { - List colours = ConfigIo.parseTomlStringList(config.get(CUSTOM_KEY)); - - for (String colour : colours) { - RainglowColour squidColour = RainglowColour.get(colour); - if (squidColour != null) { - customColours.add(squidColour); - } - } - } - - // parse server sync - boolean serverSync = true; - if (config.containsKey(SERVER_SYNC_KEY)) { - serverSync = ConfigIo.parseTomlBoolean(config.get(SERVER_SYNC_KEY)); - } +import java.util.stream.Collectors; + +@SerializedNameConvention(NamingSchemes.SNAKE_CASE) +public class RainglowConfig extends ReflectiveConfig { + @Comment("The currently active rainglow mode, which determines the possible colours for entities to spawn with.") + @Comment("If custom, will be reset to the default mode if you join a server that does not have the mode.") + public final TrackedValue mode = this.value("rainbow"); + @Comment("For server owners only: whether to sync the rainglow mode and config to clients when they join to give everyone a unified experience.") + @Comment("When this is turned off, players will not be able to see the same colours as each other, and players will see different colours each time they rejoin.") + public final TrackedValue serverSync = this.value(true); + @Comment("The rarity of coloured entities, with 0 making all entities vanilla and 100 making all entities coloured.") + public final TrackedValue> rarities = this.createMap(100); + @Comment("Toggles for disabling colours for each entity.") + public final TrackedValue> toggles = this.createMap(true); + @Comment("The custom colours to use when the mode is set to custom.") + public final TrackedValue> customColours = this.list("", RainglowMode.getDefaultCustom().stream().map(RainglowColour::getId).toArray(String[]::new)); + + private transient boolean editLocked = false; + private transient boolean initialized = false; - // parse entity toggles - for (RainglowEntity entity : RainglowEntity.values()) { - String configKey = TO_CONFIG_KEY.apply(entity); + public RainglowMode getMode() { + return RainglowMode.get(this.mode.value()); + } - if (config.containsKey(configKey)) { - entityToggles.put(entity, ConfigIo.parseTomlBoolean(config.get(configKey))); - } else { - entityToggles.put(entity, true); - } - } + public List getCustom() { + return this.customColours.value().stream().map(RainglowColour::get).collect(Collectors.toList()); + } - // parse rarity + public Map getToggles() { + Map map = new HashMap<>(); for (RainglowEntity entity : RainglowEntity.values()) { - String configKey = RARITY_CONFIG_KEY.apply(entity); - - if (config.containsKey(configKey)) { - entityRarities.put(entity, ConfigIo.parseTomlInt(config.get(configKey))); - } else { - entityRarities.put(entity, 100); - } - } - - // reset colours if parsing failed - if (customColours.isEmpty()) { - customColours = RainglowMode.getDefaultCustom(); + map.put(entity, this.isEntityEnabled(entity)); } - // set and write - this.mode = rainglowMode; - this.custom = customColours; - this.enableServerSync = serverSync; - this.save(false); - - this.isInitialised = true; + return map; } - public RainglowMode getMode() { - return this.mode; + public boolean isEntityEnabled(RainglowEntity entity) { + return this.toggles.value().get(entity.getId()); } - public List getCustom() { - return this.custom; + public void setEntityEnabled(RainglowEntity entity, boolean enabled) { + this.toggles.value().put(entity.getId(), enabled); } public int getRarity(RainglowEntity entity) { - return this.entityRarities.get(entity); + return this.rarities.value().get(entity.getId()); } - public boolean isServerSyncEnabled() { - return this.enableServerSync; + public void setRarity(RainglowEntity entity, int rarity) { + this.rarities.value().put(entity.getId(), rarity); } public boolean isEditLocked(MinecraftClient client) { @@ -124,68 +73,27 @@ public boolean isEditLocked(MinecraftClient client) { return !client.isInSingleplayer() && (client.getCurrentServerEntry() != null && this.editLocked); } - public boolean isUninitialised() { - return !this.isInitialised; - } - - public void setMode(RainglowMode mode) { - this.mode = mode; - Rainglow.setMode(mode); - } - - public void setCustom(List custom) { - this.custom = custom; - Rainglow.refreshColours(); - } - - public void setRarity(RainglowEntity entity, int rarity) { - this.entityRarities.put(entity, rarity); - } - public void setEditLocked(boolean editLocked) { this.editLocked = editLocked; } - public boolean isEntityEnabled(RainglowEntity entity) { - return this.entityToggles.get(entity); - } - - public void setEntityEnabled(RainglowEntity entity, boolean enabled) { - this.entityToggles.put(entity, enabled); - } - - public Map getEntityToggles() { - return this.entityToggles; + public boolean isInitialized() { + return this.initialized; } - public void save(boolean log) { - if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER || !this.isEditLocked(MinecraftClient.getInstance())) { - ConfigIo.writeString(MODE_KEY, this.mode.getId()); - this.saveCustom(); - ConfigIo.writeBoolean(SERVER_SYNC_KEY, this.enableServerSync); - writeEntityRarities(); - } - - // entity toggles cannot be locked by the server - this.writeEntityToggles(); - if (log) { - Rainglow.LOGGER.info("saved config!"); - } + public void setInitialized() { + this.initialized = true; } - public void saveCustom() { - ConfigIo.writeStringList(CUSTOM_KEY, this.custom); - } - - private void writeEntityToggles() { - for (Map.Entry entry : entityToggles.entrySet()) { - ConfigIo.writeBoolean(TO_CONFIG_KEY.apply(entry.getKey()), entry.getValue()); + /** + * creates a map of default values for each {@link RainglowEntity} + */ + private TrackedValue> createMap(T defaultValue) { + var builder = this.map(defaultValue); + for (RainglowEntity entity : RainglowEntity.values()) { + builder.put(entity.getId(), defaultValue); } - } - private void writeEntityRarities() { - for (Map.Entry entry : entityRarities.entrySet()) { - ConfigIo.writeInt(RARITY_CONFIG_KEY.apply(entry.getKey()), entry.getValue()); - } + return builder.build(); } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index d8d5a76..6e1e6ff 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.List; +// todo: now that config uses overrides, edit lock doesn't matter + public class RainglowConfigScreen extends SimpleOptionsScreen { // private final SpruceOption modeOption; // private final SpruceOption customOption; @@ -140,9 +142,13 @@ private static List> createColourRaritySliders() { } private void save() { - for (Option option : this.options) { - if (option instanceof DeferredSaveOption) { - ((DeferredSaveOption) option).save(); + if (Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { + sendConfigLockedToast(); + } else { + for (Option option : this.options) { + if (option instanceof DeferredSaveOption) { + ((DeferredSaveOption) option).save(); + } } } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java index b62cf7b..97a23e2 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java @@ -97,14 +97,10 @@ public boolean existsLocally() { return this.existsLocally; } - public static RainglowMode byId(String id) { + public static RainglowMode get(String id) { return MODES.get(id); } - public static RainglowMode getDefault() { - return MODES.get("rainbow"); - } - public static void addMode(RainglowMode mode) { MODES.put(mode.id, mode); } @@ -148,7 +144,7 @@ public static RainglowMode read(PacketByteBuf buf) { Text text = TextCodecs.UNLIMITED_TEXT_PACKET_CODEC.decode(buf); List colourIds = buf.readList(PacketByteBuf::readString); - return new RainglowMode(id, colourIds, text, RainglowMode.byId(id) != null); + return new RainglowMode(id, colourIds, text, RainglowMode.get(id) != null); } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java index ff6513d..6c972b4 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java @@ -1,14 +1,12 @@ package io.ix0rai.rainglow.data; import io.ix0rai.rainglow.Rainglow; -import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.network.PacketByteBuf; import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.packet.payload.CustomPayload; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.util.Identifier; import java.util.Collection; import java.util.List; @@ -18,7 +16,7 @@ public class RainglowNetworking { public static void syncConfig(ServerPlayerEntity player) { // note: client does not need to know if server sync is enabled or not // they already know that it is enabled because they are receiving this packet - ServerPlayNetworking.send(player, new ConfigSyncPayload(Rainglow.CONFIG.getMode().getId(), Rainglow.CONFIG.getCustom(), Rainglow.CONFIG.getEntityToggles())); + ServerPlayNetworking.send(player, new ConfigSyncPayload(Rainglow.CONFIG.mode.value(), Rainglow.CONFIG.getCustom(), Rainglow.CONFIG.getToggles())); } public record ConfigSyncPayload(String currentMode, List customMode, Map enabledMobs) implements CustomPayload { diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java index 2d1befc..5c91c4c 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java @@ -47,8 +47,7 @@ default void reload(ResourceManager manager) { this.log(); // load config - if (Rainglow.CONFIG.isUninitialised() || (FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT) && !Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance()))) { - Rainglow.CONFIG.reloadFromFile(); + if (!Rainglow.CONFIG.isInitialized() || (FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT) && !Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance()))) { Rainglow.setMode(Rainglow.CONFIG.getMode()); } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java index a40d18f..d93ede4 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java @@ -19,7 +19,7 @@ public void getTexture(SlimeEntity entity, CallbackInfoReturnable ci String colour = Rainglow.getColour(RainglowEntity.SLIME, entity.getDataTracker(), entity.getRandom()); // don't override if the colour is lime, use the default texture - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME) && !colour.equals(RainglowColour.LIME.getId()) || Rainglow.CONFIG.getMode().equals(RainglowMode.byId("vanilla"))) { + if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME) && !colour.equals(RainglowColour.LIME.getId()) || Rainglow.CONFIG.getMode().equals(RainglowMode.get("vanilla"))) { Identifier texture = Rainglow.getTexture(RainglowEntity.SLIME, colour); cir.setReturnValue(texture != null ? texture : Rainglow.getDefaultTexture(RainglowEntity.SLIME)); } From 5f75de279d2196fd55cb018647b752d1a185db60 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Mon, 13 May 2024 21:17:36 -0500 Subject: [PATCH 10/40] im going insane (screens) --- .../rainglow/config/DeferredSaveOption.java | 14 +- .../rainglow/config/RainglowConfigScreen.java | 266 +++++++++--------- .../client/screen/CyclingValueSetMixin.java | 24 ++ .../client/screen/SliderValueImplMixin.java | 23 ++ .../resources/assets/rainglow/lang/en_us.json | 6 +- src/main/resources/rainglow.accesswidener | 2 + src/main/resources/rainglow.mixins.json | 4 +- 7 files changed, 196 insertions(+), 143 deletions(-) create mode 100644 src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java create mode 100644 src/main/java/io/ix0rai/rainglow/mixin/client/screen/SliderValueImplMixin.java diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index d610d73..cb87415 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -12,7 +12,7 @@ import java.util.function.Consumer; public class DeferredSaveOption extends Option { - private T deferredValue; + public T deferredValue; public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback) { this(key, tooltipSupplier, textGetter, values, values.codec(), defaultValue, updateCallback); @@ -40,21 +40,21 @@ public void set(T value) { } } - public static Option createDeferredBoolean(String key, boolean defaultValue, Consumer updateCallback) { + public static DeferredSaveOption createDeferredBoolean(String key, String tooltip, boolean defaultValue, Consumer updateCallback) { return new DeferredSaveOption<>( - "rainglow.config." + key, - Option.constantTooltip(Text.translatable("rainglow.config.tooltip." + key)), + Rainglow.translatableTextKey("config." + key), + Option.constantTooltip(tooltip == null ? Rainglow.translatableText("tooltip." + key) : Rainglow.translatableText(tooltip)), (text, value) -> value ? CommonTexts.YES : CommonTexts.NO, - Option.BOOLEAN_VALUES, + BOOLEAN_VALUES, defaultValue, updateCallback ); } - public static Option createDeferredRangedInt(String key, int defaultValue, int min, int max, Consumer updateCallback) { + public static DeferredSaveOption createDeferredRangedInt(String key, String tooltip, int defaultValue, int min, int max, Consumer updateCallback) { return new DeferredSaveOption<>( Rainglow.translatableTextKey("config." + key), - Option.constantTooltip(Rainglow.translatableText("tooltip." + key)), + Option.constantTooltip(tooltip == null ? Rainglow.translatableText("tooltip." + key) : Rainglow.translatableText(tooltip)), (text, value) -> Rainglow.translatableText("value." + key, value), new Option.IntRangeValueSet(min, max), Codec.intRange(min, max), diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 6e1e6ff..4b3f9f7 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -6,8 +6,12 @@ import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; +import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.button.ButtonWidget; +import net.minecraft.client.gui.widget.button.CyclingButtonWidget; +import net.minecraft.client.gui.widget.layout.GridWidget; +import net.minecraft.client.gui.widget.layout.HeaderFooterLayoutWidget; +import net.minecraft.client.gui.widget.layout.LayoutSettings; import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; import net.minecraft.client.gui.widget.text.TextWidget; import net.minecraft.client.option.Option; @@ -20,169 +24,155 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; // todo: now that config uses overrides, edit lock doesn't matter -public class RainglowConfigScreen extends SimpleOptionsScreen { -// private final SpruceOption modeOption; -// private final SpruceOption customOption; -// private final SpruceOption resetOption; -// private final SpruceOption saveOption; +public class RainglowConfigScreen extends Screen { + private static final Text TITLE = Rainglow.translatableText("config.title"); - //private final SpruceOption colourRarityOption; - //private RainglowMode mode; - // colours to apply is saved in a variable so that it can be removed from the screen when cycling modes - // private SpruceLabelWidget coloursToApplyLabel; + private final Screen parent; + private final Map> toggles = new HashMap<>(); + private final Map> sliders = new HashMap<>(); + + private RainglowMode mode; + private boolean isConfirming; public RainglowConfigScreen(@Nullable Screen parent) { - super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), - new Option[]{ - createEntityToggles().get(0), - createColourRaritySliders().get(0), - createEntityToggles().get(1), - createColourRaritySliders().get(1), - createEntityToggles().get(2), - createColourRaritySliders().get(2), - } - ); - - - -// // mode option cycles through available modes -// // it also updates the label to show which colours will be applied -// this.modeOption = new SpruceCyclingOption(Rainglow.translatableTextKey("config.mode"), -// amount -> { -// if (!Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { -// mode = mode.cycle(); -// this.remove(this.coloursToApplyLabel); -// this.coloursToApplyLabel = createColourListLabel(Rainglow.translatableTextKey("config.colours_to_apply"), this.mode, this.width / 2 - 125, this.height / 4 + 40); -// this.addDrawable(this.coloursToApplyLabel); -// } else { -// sendConfigLockedToast(); -// } -// }, -// option -> option.getDisplayText(mode.getText()), -// Rainglow.translatableText("tooltip.mode", -// List.of(RainglowMode.values()) -// ) -// ); -// -// // opens a screen to toggle which colours are applied in custom mode -// this.customOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.custom"), -// btn -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this)) -// ); -// -// // toggles whether entities are rainbow -// for (int i = 0; i < RainglowEntity.values().length; i ++) { -// RainglowEntity entity = RainglowEntity.values()[i]; -// -// this.entityToggles[i] = createEntityToggle( -// entity, -// () -> Rainglow.CONFIG.isEntityEnabled(entity), -// enabled -> Rainglow.CONFIG.setEntityEnabled(entity, enabled) -// ); -// } -// -// this.colourRarityOption = new SpruceIntegerInputOption(Rainglow.translatableTextKey("config.rarity"), -// Rainglow.CONFIG::getRarity, -// Rainglow.CONFIG::setRarity, -// Rainglow.translatableText("tooltip.rarity") -// ); -// -// // resets the config to default values -// this.resetOption = SpruceSimpleActionOption.reset(btn -> { -// MinecraftClient client = MinecraftClient.getInstance(); -// if (!Rainglow.CONFIG.isEditLocked(client)) { -// this.mode = RainglowMode.getDefault(); -// this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); -// } else { -// sendConfigLockedToast(); -// } -// }); -// -// // saves values to config file -// this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), -// buttonWidget -> { -// this.closeScreen(); -// Rainglow.CONFIG.setMode(this.mode); -// Rainglow.CONFIG.save(true); -// } -// ); + super(TITLE); + this.parent = parent; + this.mode = RainglowMode.get(Rainglow.CONFIG.mode.value()); } - private static List> createEntityToggles() { - List> toggles = new ArrayList<>(); + @Override + public void init() { + HeaderFooterLayoutWidget headerFooterWidget = new HeaderFooterLayoutWidget(this, 61, 33); + + if (!this.isConfirming) { + // header + LinearLayoutWidget linearLayoutWidget = headerFooterWidget.addToHeader(LinearLayoutWidget.createVertical().setSpacing(20)); + linearLayoutWidget.add(new TextWidget(TITLE, this.textRenderer), LayoutSettings::alignHorizontallyCenter); + linearLayoutWidget.add(createModeButton()); + + // contents + GridWidget gridWidget = new GridWidget(); + gridWidget.getDefaultSettings().setHorizontalPadding(4).setBottomPadding(4).alignHorizontallyCenter(); + GridWidget.AdditionHelper miscAdditionHelper = gridWidget.createAdditionHelper(2); + for (RainglowEntity entity : RainglowEntity.values()) { + DeferredSaveOption entityToggle = createEntityToggle(entity); + miscAdditionHelper.add(entityToggle.createButton(MinecraftClient.getInstance().options)); + entityToggle.set(entityToggle.deferredValue); + + miscAdditionHelper.add(createColourRaritySlider(entity).createButton(MinecraftClient.getInstance().options)); + } + + headerFooterWidget.addToContents(gridWidget); + + // footer + LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); + linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); + linearLayout.add(ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { + this.save(); + this.closeScreen(true); + }).build()); + } else { + LinearLayoutWidget titleWidget = headerFooterWidget.addToHeader(LinearLayoutWidget.createVertical().setSpacing(20)); + titleWidget.add(new TextWidget(this.title, this.textRenderer), LayoutSettings::alignHorizontallyCenter); + + LinearLayoutWidget contentWidget = headerFooterWidget.addToContents(new LinearLayoutWidget(250, 100, LinearLayoutWidget.Orientation.VERTICAL).setSpacing(8)); + contentWidget.add(new TextWidget(Rainglow.translatableText("config.unsaved_warning"), this.textRenderer), LayoutSettings::alignHorizontallyCenter); + + LinearLayoutWidget buttons = new LinearLayoutWidget(250, 20, LinearLayoutWidget.Orientation.HORIZONTAL); + buttons.add(ButtonWidget.builder(Rainglow.translatableText("config.continue_editing"), (buttonWidget) -> { + this.isConfirming = false; + this.clearAndInit(); + }).build()); + buttons.add(ButtonWidget.builder(CommonTexts.YES, (buttonWidget) -> MinecraftClient.getInstance().setScreen(this.parent)).build()); - for (RainglowEntity entity : RainglowEntity.values()) { - toggles.add(DeferredSaveOption.createDeferredBoolean( - "enable_" + entity.getId(), - Rainglow.CONFIG.isEntityEnabled(entity), - enabled -> Rainglow.CONFIG.setEntityEnabled(entity, enabled) - )); + contentWidget.add(buttons, LayoutSettings::alignHorizontallyCenter); } - return toggles; + headerFooterWidget.visitWidgets(this::addDrawableSelectableElement); + headerFooterWidget.arrangeElements(); } - private static List> createColourRaritySliders() { - List> sliders = new ArrayList<>(); - - for (RainglowEntity entity : RainglowEntity.values()) { - sliders.add(DeferredSaveOption.createDeferredRangedInt( - entity.getId() + "_rarity", - Rainglow.CONFIG.getRarity(entity), - 0, - 100, - rarity -> Rainglow.CONFIG.setRarity(entity, rarity) - )); - } + private DeferredSaveOption createEntityToggle(RainglowEntity entity) { + return toggles.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredBoolean( + "enable_" + e.getId(), + "tooltip.entity_toggle", + Rainglow.CONFIG.isEntityEnabled(e), + enabled -> Rainglow.CONFIG.setEntityEnabled(e, enabled) + )); + } - return sliders; + private DeferredSaveOption createColourRaritySlider(RainglowEntity entity) { + return sliders.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredRangedInt( + e.getId() + "_rarity", + "tooltip.rarity", + Rainglow.CONFIG.getRarity(e), + 0, + 100, + rarity -> Rainglow.CONFIG.setRarity(e, rarity) + )); + } + + public CyclingButtonWidget createModeButton() { + return CyclingButtonWidget.builder(RainglowMode::getText) + .values(RainglowMode.values()) + .initially(this.mode) + .tooltip(this::createColourListLabel) + .build( + 0, + 0, + 300, + 20, + Rainglow.translatableText("config.mode"), + (cyclingButtonWidget, mode) -> { + RainglowConfigScreen.this.mode = mode; + } + ); } private void save() { if (Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { sendConfigLockedToast(); } else { - for (Option option : this.options) { + Collection> options = new ArrayList<>(this.sliders.values()); + options.addAll(this.toggles.values()); + + for (Option option : options) { if (option instanceof DeferredSaveOption) { ((DeferredSaveOption) option).save(); } } - } - } - @Override - protected void method_31387() { - LinearLayoutWidget linearLayout = this.field_49503.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); - linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); - linearLayout.add(ButtonWidget.builder(CommonTexts.YES, button -> { - this.save(); - this.closeScreen(); - }).build()); - this.field_49503.visitWidgets(this::addDrawableSelectableElement); - this.repositionElements(); + Rainglow.CONFIG.mode.setValue(this.mode.getId()); + } } - private TextWidget createColourListLabel(String translationKey, RainglowMode mode, int x, int y) { + private Tooltip createColourListLabel(RainglowMode mode) { // creates a label and appends all the colours that will be applied in the given mode - StringBuilder text = new StringBuilder(Language.getInstance().get(translationKey)); + StringBuilder text = new StringBuilder(Language.getInstance().get(Rainglow.translatableTextKey("config.colours_to_apply"))); int maxDisplayedColourCount = 16; + int maxColoursPerLine = 4; - for (int i = 0; i < mode.getColours().size(); i += 2) { - RainglowColour colour = mode.getColours().get(i); - + for (int i = 0; i < mode.getColours().size(); i += maxColoursPerLine) { if (i < maxDisplayedColourCount) { - String colour1 = Language.getInstance().get(Rainglow.translatableTextKey("colour." + colour.getId())); - String colour2 = ""; - if (i + 1 <= mode.getColours().size() - 1) { - colour2 = Language.getInstance().get(Rainglow.translatableTextKey("colour." + mode.getColours().get(i + 1).getId())); - } + text.append("\n"); - boolean appendComma = i + 2 < mode.getColours().size(); + int coloursLeft = mode.getColours().size() - i; + int coloursToDisplay = Math.min(coloursLeft, maxColoursPerLine); - text.append("\n").append(colour1).append(colour2.isEmpty() ? "" : ", ").append(colour2).append(appendComma ? "," : ""); + for (int j = 0; j < coloursToDisplay; j++) { + RainglowColour currentColour = mode.getColours().get(i + j); + text.append(Language.getInstance().get(Rainglow.translatableTextKey("colour." + currentColour.getId()))); + if (j < coloursToDisplay - 1) { + text.append(", "); + } + } } else { text.append("\n... ").append(mode.getColours().size() - maxDisplayedColourCount).append(" ").append(Language.getInstance().get(Rainglow.translatableTextKey("config.more"))); } @@ -190,7 +180,21 @@ private TextWidget createColourListLabel(String translationKey, RainglowMode mod // set colour to the mode's text colour Style style = Style.EMPTY.withColor(mode.getText().getStyle().getColor()); - return new TextWidget(Text.literal(text.toString()).setStyle(style), MinecraftClient.getInstance().textRenderer); + return Tooltip.create(Text.literal(text.toString()).setStyle(style)); + } + + @Override + public void closeScreen() { + this.closeScreen(false); + } + + public void closeScreen(boolean saved) { + if (!saved) { + this.isConfirming = true; + this.clearAndInit(); + } else { + MinecraftClient.getInstance().setScreen(this.parent); + } } private static void sendConfigLockedToast() { diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java new file mode 100644 index 0000000..57ee0ad --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java @@ -0,0 +1,24 @@ +package io.ix0rai.rainglow.mixin.client.screen; + +import com.llamalad7.mixinextras.sugar.Local; +import io.ix0rai.rainglow.config.DeferredSaveOption; +import net.minecraft.client.option.Option; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(Option.CyclingValueSet.class) +public interface CyclingValueSetMixin { + @ModifyArg( + method = "method_42723", + remap = false, + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/widget/button/CyclingButtonWidget$Builder;initially(Ljava/lang/Object;)Lnet/minecraft/client/gui/widget/button/CyclingButtonWidget$Builder;" + ), + index = 0 + ) + private T updateOptionValue(T value, @Local(argsOnly = true) Option option) { + return option instanceof DeferredSaveOption deferred ? deferred.deferredValue : value; + } +} diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/screen/SliderValueImplMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/SliderValueImplMixin.java new file mode 100644 index 0000000..dc24cdd --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/SliderValueImplMixin.java @@ -0,0 +1,23 @@ +package io.ix0rai.rainglow.mixin.client.screen; + +import com.llamalad7.mixinextras.sugar.Local; +import io.ix0rai.rainglow.config.DeferredSaveOption; +import net.minecraft.client.option.Option; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(Option.OptionSliderWidgetImpl.class) +public class SliderValueImplMixin { + @ModifyArg( + method = "", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/option/Option$SliderValueSet;toSliderValue(Ljava/lang/Object;)D" + ), + index = 0 + ) + private static T updateOptionValue(T value, @Local(argsOnly = true) Option option) { + return option instanceof DeferredSaveOption deferred ? deferred.deferredValue : value; + } +} diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index 3a7f2ca..437a832 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -13,12 +13,10 @@ "rainglow.config.server_locked_title": "Config is locked by server", "rainglow.config.server_locked_description": "You can't change the mode while the config is locked!", "rainglow.value.slime_rarity": "Slime rarity: %s", - "rainglow.tooltip.slime_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", + "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.value.allay_rarity": "Allay rarity: %s", - "rainglow.tooltip.allay_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.value.glow_squid_rarity": "Glow squid rarity: %s", - "rainglow.tooltip.glow_squid_rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", - "rainglow.tooltip.mode": "Rainglow Mode dictates which colours are currently available for squids", + "rainglow.tooltip.mode": "Determines the possible colours for entities to have.", "rainglow.config.cannot_be_loaded_outside_world": "Rainglow config cannot be edited through the GUI before loading a world!", "rainglow.mode.rainbow": "Rainbow", "rainglow.mode.all_colours": "All Colours", diff --git a/src/main/resources/rainglow.accesswidener b/src/main/resources/rainglow.accesswidener index 654039e..9225a7c 100644 --- a/src/main/resources/rainglow.accesswidener +++ b/src/main/resources/rainglow.accesswidener @@ -8,6 +8,8 @@ accessible field net/minecraft/client/option/Option value Ljava/lang/Object; accessible field net/minecraft/client/option/Option defaultValue Ljava/lang/Object; accessible field net/minecraft/client/option/Option updateCallback Ljava/util/function/Consumer; accessible field net/minecraft/client/option/Option text Lnet/minecraft/text/Text; +accessible field net/minecraft/client/option/Option valueToText Ljava/util/function/Function; +accessible class net/minecraft/client/option/Option$CyclingValueSet accessible method net/minecraft/client/particle/GlowParticle (Lnet/minecraft/client/world/ClientWorld;DDDDDDLnet/minecraft/client/particle/SpriteProvider;)V accessible field net/minecraft/client/particle/GlowParticle RANDOM Lnet/minecraft/util/random/RandomGenerator; diff --git a/src/main/resources/rainglow.mixins.json b/src/main/resources/rainglow.mixins.json index 6258dd8..9836a06 100644 --- a/src/main/resources/rainglow.mixins.json +++ b/src/main/resources/rainglow.mixins.json @@ -17,7 +17,9 @@ "client.GlowSquidEntityRendererMixin", "client.SlimeEntityRendererMixin", "client.SlimeParticleMixin", - "client.SquidInkParticleMixin" + "client.SquidInkParticleMixin", + "client.screen.CyclingValueSetMixin", + "client.screen.SliderValueImplMixin" ], "injectors": { "defaultRequire": 1 From ca10bc1f816b26d5b99fca0bc19873c3711f418f Mon Sep 17 00:00:00 2001 From: ix0rai Date: Tue, 14 May 2024 21:42:29 -0500 Subject: [PATCH 11/40] screen work --- .../rainglow/config/CustomModeScreen.java | 16 +----- .../rainglow/config/DeferredSaveOption.java | 14 ----- .../rainglow/config/RainglowConfigScreen.java | 57 ++++++++++--------- .../resources/assets/rainglow/lang/de_de.json | 7 ++- .../resources/assets/rainglow/lang/en_us.json | 5 +- .../resources/assets/rainglow/lang/fr_fr.json | 1 - 6 files changed, 41 insertions(+), 59 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 08e1e26..36a8ca6 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -1,21 +1,11 @@ package io.ix0rai.rainglow.config; -import io.ix0rai.rainglow.Rainglow; -import io.ix0rai.rainglow.data.RainglowColour; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.option.SimpleOptionsScreen; -import net.minecraft.client.option.GameOptions; -import net.minecraft.client.option.Option; import net.minecraft.text.Text; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; - -public class CustomModeScreen extends SimpleOptionsScreen { - public CustomModeScreen(Screen parent, GameOptions gameOptions, Text title, Option[] options) { - super(parent, gameOptions, title, options); +public class CustomModeScreen extends Screen { + public CustomModeScreen(Screen parent) { + super(Text.of("fa")); } // private final SpruceOption clearOption; // private final SpruceOption saveOption; diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index cb87415..83094a2 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -3,10 +3,8 @@ import com.mojang.serialization.Codec; import io.ix0rai.rainglow.Rainglow; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.Option; import net.minecraft.text.CommonTexts; -import net.minecraft.text.Text; import java.util.Objects; import java.util.function.Consumer; @@ -63,18 +61,6 @@ public static DeferredSaveOption createDeferredRangedInt(String key, St ); } - public static Option createDeferredRangedDouble(String key, double defaultValue, double min, double max, Consumer updateCallback) { - return new DeferredSaveOption<>( - "rainglow.config." + key, - Option.constantTooltip(Text.translatable("rainglow.tooltip." + key)), - (text, value) -> GameOptions.getGenericValueText(text, Text.translatable("rainglow.value." + key, value)), - new Option.IntRangeValueSet((int) (min * 10), (int) (max * 10)).withModifier(i -> (double) i / 10.0, double_ -> (int) (double_ * 10.0)), - Codec.doubleRange(min, max), - defaultValue, - updateCallback - ); - } - public void save() { this.value = this.deferredValue; this.updateCallback.accept(this.value); diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 4b3f9f7..5ccc972 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -26,11 +26,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; -// todo: now that config uses overrides, edit lock doesn't matter - public class RainglowConfigScreen extends Screen { private static final Text TITLE = Rainglow.translatableText("config.title"); @@ -50,26 +47,35 @@ public RainglowConfigScreen(@Nullable Screen parent) { @Override public void init() { HeaderFooterLayoutWidget headerFooterWidget = new HeaderFooterLayoutWidget(this, 61, 33); + LinearLayoutWidget headerLayout = headerFooterWidget.addToHeader(LinearLayoutWidget.createVertical().setSpacing(8)); if (!this.isConfirming) { // header - LinearLayoutWidget linearLayoutWidget = headerFooterWidget.addToHeader(LinearLayoutWidget.createVertical().setSpacing(20)); - linearLayoutWidget.add(new TextWidget(TITLE, this.textRenderer), LayoutSettings::alignHorizontallyCenter); - linearLayoutWidget.add(createModeButton()); + headerLayout.add(new TextWidget(TITLE, this.textRenderer), settings -> settings.alignHorizontallyCenter().alignVerticallyTop().setPadding(12)); + headerLayout.add(createModeButton(), LayoutSettings::alignVerticallyBottom); + if (MinecraftClient.getInstance().world == null) { + headerLayout.add(new TextWidget(Rainglow.translatableText("config.no_world"), this.textRenderer).setTextColor(0xc21919), LayoutSettings::alignHorizontallyCenter); + } // contents + LinearLayoutWidget contentLayout = LinearLayoutWidget.createVertical(); + GridWidget gridWidget = new GridWidget(); gridWidget.getDefaultSettings().setHorizontalPadding(4).setBottomPadding(4).alignHorizontallyCenter(); - GridWidget.AdditionHelper miscAdditionHelper = gridWidget.createAdditionHelper(2); + + GridWidget.AdditionHelper mainAdditionHelper = gridWidget.createAdditionHelper(2); for (RainglowEntity entity : RainglowEntity.values()) { DeferredSaveOption entityToggle = createEntityToggle(entity); - miscAdditionHelper.add(entityToggle.createButton(MinecraftClient.getInstance().options)); + mainAdditionHelper.add(entityToggle.createButton(MinecraftClient.getInstance().options)); entityToggle.set(entityToggle.deferredValue); - miscAdditionHelper.add(createColourRaritySlider(entity).createButton(MinecraftClient.getInstance().options)); + mainAdditionHelper.add(createColourRaritySlider(entity).createButton(MinecraftClient.getInstance().options)); } - headerFooterWidget.addToContents(gridWidget); + contentLayout.add(gridWidget); + contentLayout.add(ButtonWidget.builder(Rainglow.translatableText("config.custom_mode"), button -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this))).width(308).position(4, 0).build(), LayoutSettings.create().setPadding(4, 0)); + + headerFooterWidget.addToContents(contentLayout); // footer LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); @@ -79,9 +85,6 @@ public void init() { this.closeScreen(true); }).build()); } else { - LinearLayoutWidget titleWidget = headerFooterWidget.addToHeader(LinearLayoutWidget.createVertical().setSpacing(20)); - titleWidget.add(new TextWidget(this.title, this.textRenderer), LayoutSettings::alignHorizontallyCenter); - LinearLayoutWidget contentWidget = headerFooterWidget.addToContents(new LinearLayoutWidget(250, 100, LinearLayoutWidget.Orientation.VERTICAL).setSpacing(8)); contentWidget.add(new TextWidget(Rainglow.translatableText("config.unsaved_warning"), this.textRenderer), LayoutSettings::alignHorizontallyCenter); @@ -127,30 +130,24 @@ public CyclingButtonWidget createModeButton() { .build( 0, 0, - 300, + 308, 20, Rainglow.translatableText("config.mode"), - (cyclingButtonWidget, mode) -> { - RainglowConfigScreen.this.mode = mode; - } + (cyclingButtonWidget, mode) -> RainglowConfigScreen.this.mode = mode ); } private void save() { - if (Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { - sendConfigLockedToast(); - } else { - Collection> options = new ArrayList<>(this.sliders.values()); - options.addAll(this.toggles.values()); + Collection> options = new ArrayList<>(this.sliders.values()); + options.addAll(this.toggles.values()); - for (Option option : options) { - if (option instanceof DeferredSaveOption) { - ((DeferredSaveOption) option).save(); - } + for (Option option : options) { + if (option instanceof DeferredSaveOption) { + ((DeferredSaveOption) option).save(); } - - Rainglow.CONFIG.mode.setValue(this.mode.getId()); } + + Rainglow.CONFIG.mode.setValue(this.mode.getId()); } private Tooltip createColourListLabel(RainglowMode mode) { @@ -193,6 +190,10 @@ public void closeScreen(boolean saved) { this.isConfirming = true; this.clearAndInit(); } else { + if (Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { + sendConfigLockedToast(); + } + MinecraftClient.getInstance().setScreen(this.parent); } } diff --git a/src/main/resources/assets/rainglow/lang/de_de.json b/src/main/resources/assets/rainglow/lang/de_de.json index 8f59465..5e654b1 100644 --- a/src/main/resources/assets/rainglow/lang/de_de.json +++ b/src/main/resources/assets/rainglow/lang/de_de.json @@ -11,7 +11,6 @@ "rainglow.config.enable_slime": "Regenbogenfarbener Schleim", "rainglow.config.enable_allay": "Regenbogenfarbene Hilfsgeister", "rainglow.config.server_locked_title": "Konfiguration vom Server gesperrt", - "rainglow.config.server_locked_description": "Du kannst dies nicht ändern, solange der Server diese Konfiguration gesperrt hat!", "rainglow.tooltip.mode": "Der Modus bestimmt, welche Farben gerade aktiviert sind", "rainglow.config.cannot_be_loaded_outside_world": "Die Rainglow Konfiguration kann nicht außerhalb einer Welt geladen werden!", "rainglow.mode.rainbow": "Regenbogen", @@ -44,5 +43,9 @@ "rainglow.colour.light_gray": "Hellgrau", "rainglow.colour.lime": "Hellgrün", "rainglow.colour.magenta": "Magenta", - "rainglow.colour.indigo": "Indigo" + "rainglow.colour.indigo": "Indigo", + "rainglow.value.slime_rarity": "Slime rarity: %s", + "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", + "rainglow.value.allay_rarity": "Allay rarity: %s", + "rainglow.value.glow_squid_rarity": "Glow squid rarity: %s" } diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index 437a832..629ad1d 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -11,7 +11,10 @@ "rainglow.config.enable_slime": "Rainbow slimes", "rainglow.config.enable_allay": "Rainbow allays", "rainglow.config.server_locked_title": "Config is locked by server", - "rainglow.config.server_locked_description": "You can't change the mode while the config is locked!", + "rainglow.config.server_locked_description": "Changes applied will only be visible once you join a world without a locked config.", + "rainglow.config.no_world": "No world loaded: only default modes are available.", + "rainglow.config.unsaved_warning": "Config is not saved and changes will be lost! Leave without saving?", + "rainglow.config.continue_editing": "Continue editing", "rainglow.value.slime_rarity": "Slime rarity: %s", "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.value.allay_rarity": "Allay rarity: %s", diff --git a/src/main/resources/assets/rainglow/lang/fr_fr.json b/src/main/resources/assets/rainglow/lang/fr_fr.json index 6394fc3..3a630ba 100644 --- a/src/main/resources/assets/rainglow/lang/fr_fr.json +++ b/src/main/resources/assets/rainglow/lang/fr_fr.json @@ -11,7 +11,6 @@ "rainglow.config.enable_slime": "Slimes arc-en-ciel", "rainglow.config.enable_allay": "Allays arc-en-ciel", "rainglow.config.server_locked_title": "La configuration est verrouillée par le serveur", - "rainglow.config.server_locked_description": "Vous ne pouvez pas changer le mode tant que la configuration est verrouillée !", "rainglow.tooltip.mode": "Le mode Rainglow dicte quelles couleurs sont actuellement disponibles pour les poulpes", "rainglow.config.cannot_be_loaded_outside_world": "La configuration de Rainglow ne peut pas être modifiée par l'interface utilisateur tant qu'un monde n'est pas chargé !", "rainglow.mode.rainbow": "Arc-en-ciel", From e7cda2d6f9de0960384c1860d2a586bd76ddf59b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 15 May 2024 22:40:16 -0500 Subject: [PATCH 12/40] screen work 2: custom mode --- .../java/io/ix0rai/rainglow/Rainglow.java | 5 + .../rainglow/config/CustomModeScreen.java | 149 +++++++++--------- .../rainglow/config/DeferredSaveOption.java | 31 ++-- .../rainglow/config/RainglowConfigScreen.java | 36 +++-- .../resources/assets/rainglow/lang/en_us.json | 9 +- 5 files changed, 120 insertions(+), 110 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 1efb13b..c351ed4 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -67,6 +67,11 @@ public static Identifier id(String id) { } public static void setMode(RainglowMode mode) { + if (mode == null) { + mode = RainglowMode.get("rainbow"); + LOGGER.warn("attempted to load missing mode, resetting to rainbow"); + } + GLOWSQUID_TEXTURES.clear(); ALLAY_TEXTURES.clear(); SLIME_TEXTURES.clear(); diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 36a8ca6..6a32b05 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -1,86 +1,79 @@ package io.ix0rai.rainglow.config; +import io.ix0rai.rainglow.Rainglow; +import io.ix0rai.rainglow.data.RainglowColour; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.option.GameOptionsScreen; +import net.minecraft.client.gui.widget.button.ButtonWidget; +import net.minecraft.client.gui.widget.layout.GridWidget; +import net.minecraft.client.gui.widget.layout.HeaderFooterLayoutWidget; +import net.minecraft.client.gui.widget.layout.LayoutSettings; +import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; +import net.minecraft.client.gui.widget.list.ButtonListWidget; +import net.minecraft.client.gui.widget.text.TextWidget; +import net.minecraft.client.option.Option; +import net.minecraft.text.CommonTexts; import net.minecraft.text.Text; -public class CustomModeScreen extends Screen { +import java.util.ArrayList; +import java.util.List; + +public class CustomModeScreen extends GameOptionsScreen { + private final ButtonWidget saveButton; + private final List> options = new ArrayList<>(); + + private static final Text TITLE = Rainglow.translatableText("config.custom"); + public CustomModeScreen(Screen parent) { - super(Text.of("fa")); + super(parent, MinecraftClient.getInstance().options, TITLE); + this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { + this.save(); + this.closeScreen(); + }).build(); + this.saveButton.active = false; + } + + private void createColourToggles() { + this.options.clear(); + + for (RainglowColour colour : RainglowColour.values()) { + this.options.add(DeferredSaveOption.createDeferredBoolean( + "colour." + colour.getId(), + null, + Rainglow.CONFIG.getCustom().contains(colour), + enabled -> { + if (enabled) { + Rainglow.CONFIG.getCustom().add(colour); + } else { + Rainglow.CONFIG.getCustom().remove(colour); + } + }, + enabled -> this.saveButton.active = true + )); + } + } + + private void save() { + for (DeferredSaveOption option : this.options) { + option.save(); + } + } + + @Override + public void init() { + HeaderFooterLayoutWidget headerFooterWidget = new HeaderFooterLayoutWidget(this, 61, 33); + headerFooterWidget.addToHeader(new TextWidget(TITLE, this.textRenderer), settings -> settings.alignHorizontallyCenter().setBottomPadding(28)); + + ButtonListWidget buttonListWidget = headerFooterWidget.addToContents(new ButtonListWidget(this.client, this.width, this.height, this)); + createColourToggles(); + buttonListWidget.addEntries(this.options.toArray(new Option[0])); + + LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); + linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); + linearLayout.add(this.saveButton); + + headerFooterWidget.visitWidgets(this::addDrawableSelectableElement); + headerFooterWidget.arrangeElements(); } -// private final SpruceOption clearOption; -// private final SpruceOption saveOption; -// private final SpruceBooleanOption[] colourToggles = new SpruceBooleanOption[RainglowColour.values().length]; -// private final boolean[] toggleStates = new boolean[RainglowColour.values().length]; -// -// public CustomModeScreen(@Nullable Screen parent) { -// super(parent, MinecraftClient.getInstance().options, Rainglow.translatableText("config.title"), -// -// ); -// -// // todo subclass option to allow saving via a save button -// // ephemeral value, not saved until a specific method is called (will happen on save pressed) -// -// // create toggles for each colour -// for (int i = 0; i < RainglowColour.values().length; i ++) { -// final RainglowColour colour = RainglowColour.values()[i]; -// final int index = i; -// -// toggleStates[index] = Rainglow.CONFIG.getCustom().contains(colour); -// -// colourToggles[index] = new SpruceBooleanOption(Rainglow.translatableTextKey("colour." + colour.getId()), -// () -> toggleStates[index], -// enable -> toggleStates[index] = enable, -// null, -// true -// ); -// } -// -// // toggles all colours to false -// this.clearOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.clear"), -// btn -> { -// for (int i = 0; i < RainglowColour.values().length; i ++) { -// toggleStates[i] = false; -// } -// -// MinecraftClient client = MinecraftClient.getInstance(); -// this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); -// }); -// -// // writes all the toggled colours to the config and reloads custom mode -// this.saveOption = SpruceSimpleActionOption.of(Rainglow.translatableTextKey("config.save"), -// buttonWidget -> { -// List newCustom = new ArrayList<>(); -// -// for (int i = 0; i < RainglowColour.values().length; i ++) { -// if (toggleStates[i]) { -// newCustom.add(RainglowColour.values()[i]); -// } -// } -// -// Rainglow.CONFIG.setCustom(newCustom); -// Rainglow.CONFIG.saveCustom(); -// this.closeScreen(); -// } -// ); -// } -// -// @Override -// protected void init() { -// super.init(); -// -// // create a list of toggles for each colour -// SpruceOptionListWidget options = new SpruceOptionListWidget(Position.of(0, 22), this.width, this.height - (35 + 22)); -// for (int i = 0; i < RainglowColour.values().length; i += 2) { -// SpruceOption secondToggle = null; -// if (i + 1 < RainglowColour.values().length) { -// secondToggle = colourToggles[i + 1]; -// } -// options.addOptionEntry(colourToggles[i], secondToggle); -// } -// this.addDrawableSelectableElement(options); -// -// // save and clear buttons -// this.addDrawableSelectableElement(this.clearOption.createWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150)); -// this.addDrawableSelectableElement(this.saveOption.createWidget(Position.of(this, this.width / 2 - 155 + 160, this.height - 29), 150)); -// } } diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 83094a2..ab9c2cd 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -11,14 +11,16 @@ public class DeferredSaveOption extends Option { public T deferredValue; + private Consumer clickCallback; - public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback) { - this(key, tooltipSupplier, textGetter, values, values.codec(), defaultValue, updateCallback); + public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback, Consumer clickCallback) { + this(key, tooltipSupplier, textGetter, values, values.codec(), defaultValue, updateCallback, clickCallback); } - public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, Codec codec, T defaultValue, Consumer updateCallback) { + public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, Codec codec, T defaultValue, Consumer updateCallback, Consumer clickCallback) { super(key, tooltipSupplier, textGetter, values, codec, defaultValue, updateCallback); this.deferredValue = this.value; + this.clickCallback = clickCallback; } @Override @@ -33,31 +35,34 @@ public void set(T value) { } else { if (!Objects.equals(this.value, object)) { this.deferredValue = object; - // note: callback is called on save + this.clickCallback.accept(object); + // note: update callback is called on save } } } - public static DeferredSaveOption createDeferredBoolean(String key, String tooltip, boolean defaultValue, Consumer updateCallback) { + public static DeferredSaveOption createDeferredBoolean(String key, String tooltip, boolean defaultValue, Consumer updateCallback, Consumer clickCallback) { return new DeferredSaveOption<>( - Rainglow.translatableTextKey("config." + key), - Option.constantTooltip(tooltip == null ? Rainglow.translatableText("tooltip." + key) : Rainglow.translatableText(tooltip)), + Rainglow.translatableTextKey(key), + tooltip != null ? Option.constantTooltip(Rainglow.translatableText("tooltip." + key)) : Option.emptyTooltip(), (text, value) -> value ? CommonTexts.YES : CommonTexts.NO, BOOLEAN_VALUES, defaultValue, - updateCallback + updateCallback, + clickCallback ); } - public static DeferredSaveOption createDeferredRangedInt(String key, String tooltip, int defaultValue, int min, int max, Consumer updateCallback) { + public static DeferredSaveOption createDeferredRangedInt(String key, String tooltip, int defaultValue, int min, int max, Consumer updateCallback, Consumer clickCallback) { return new DeferredSaveOption<>( - Rainglow.translatableTextKey("config." + key), - Option.constantTooltip(tooltip == null ? Rainglow.translatableText("tooltip." + key) : Rainglow.translatableText(tooltip)), - (text, value) -> Rainglow.translatableText("value." + key, value), + Rainglow.translatableTextKey(key), + tooltip != null ? Option.constantTooltip(Rainglow.translatableText("tooltip." + key)) : Option.emptyTooltip(), + (text, value) -> Rainglow.translatableText(key + ".value", value), new Option.IntRangeValueSet(min, max), Codec.intRange(min, max), defaultValue, - updateCallback + updateCallback, + clickCallback ); } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 5ccc972..9a5273a 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -34,6 +34,7 @@ public class RainglowConfigScreen extends Screen { private final Screen parent; private final Map> toggles = new HashMap<>(); private final Map> sliders = new HashMap<>(); + private final ButtonWidget saveButton; private RainglowMode mode; private boolean isConfirming; @@ -42,6 +43,11 @@ public RainglowConfigScreen(@Nullable Screen parent) { super(TITLE); this.parent = parent; this.mode = RainglowMode.get(Rainglow.CONFIG.mode.value()); + this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { + this.save(); + this.closeScreen(true); + }).build(); + this.saveButton.active = false; } @Override @@ -73,22 +79,19 @@ public void init() { } contentLayout.add(gridWidget); - contentLayout.add(ButtonWidget.builder(Rainglow.translatableText("config.custom_mode"), button -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this))).width(308).position(4, 0).build(), LayoutSettings.create().setPadding(4, 0)); + contentLayout.add(ButtonWidget.builder(Rainglow.translatableText("config.custom"), button -> MinecraftClient.getInstance().setScreen(new CustomModeScreen(this))).width(308).position(4, 0).build(), LayoutSettings.create().setPadding(4, 0)); headerFooterWidget.addToContents(contentLayout); // footer LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); - linearLayout.add(ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { - this.save(); - this.closeScreen(true); - }).build()); + linearLayout.add(this.saveButton); } else { LinearLayoutWidget contentWidget = headerFooterWidget.addToContents(new LinearLayoutWidget(250, 100, LinearLayoutWidget.Orientation.VERTICAL).setSpacing(8)); contentWidget.add(new TextWidget(Rainglow.translatableText("config.unsaved_warning"), this.textRenderer), LayoutSettings::alignHorizontallyCenter); - LinearLayoutWidget buttons = new LinearLayoutWidget(250, 20, LinearLayoutWidget.Orientation.HORIZONTAL); + LinearLayoutWidget buttons = new LinearLayoutWidget(250, 20, LinearLayoutWidget.Orientation.HORIZONTAL).setSpacing(8); buttons.add(ButtonWidget.builder(Rainglow.translatableText("config.continue_editing"), (buttonWidget) -> { this.isConfirming = false; this.clearAndInit(); @@ -104,21 +107,23 @@ public void init() { private DeferredSaveOption createEntityToggle(RainglowEntity entity) { return toggles.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredBoolean( - "enable_" + e.getId(), - "tooltip.entity_toggle", - Rainglow.CONFIG.isEntityEnabled(e), - enabled -> Rainglow.CONFIG.setEntityEnabled(e, enabled) + "config.enable_" + e.getId(), + "tooltip.entity_toggle", + Rainglow.CONFIG.isEntityEnabled(e), + enabled -> Rainglow.CONFIG.setEntityEnabled(e, enabled), + enabled -> this.saveButton.active = true )); } private DeferredSaveOption createColourRaritySlider(RainglowEntity entity) { return sliders.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredRangedInt( - e.getId() + "_rarity", + "config." + e.getId() + "_rarity", "tooltip.rarity", Rainglow.CONFIG.getRarity(e), 0, 100, - rarity -> Rainglow.CONFIG.setRarity(e, rarity) + rarity -> Rainglow.CONFIG.setRarity(e, rarity), + rarity -> this.saveButton.active = true )); } @@ -133,7 +138,10 @@ public CyclingButtonWidget createModeButton() { 308, 20, Rainglow.translatableText("config.mode"), - (cyclingButtonWidget, mode) -> RainglowConfigScreen.this.mode = mode + (cyclingButtonWidget, mode) -> { + this.saveButton.active = true; + RainglowConfigScreen.this.mode = mode; + } ); } @@ -186,7 +194,7 @@ public void closeScreen() { } public void closeScreen(boolean saved) { - if (!saved) { + if (!saved && this.saveButton.active) { this.isConfirming = true; this.clearAndInit(); } else { diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index 629ad1d..7fc9254 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -15,12 +15,11 @@ "rainglow.config.no_world": "No world loaded: only default modes are available.", "rainglow.config.unsaved_warning": "Config is not saved and changes will be lost! Leave without saving?", "rainglow.config.continue_editing": "Continue editing", - "rainglow.value.slime_rarity": "Slime rarity: %s", + "rainglow.config.slime_rarity.value": "Slime rarity: %s", "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", - "rainglow.value.allay_rarity": "Allay rarity: %s", - "rainglow.value.glow_squid_rarity": "Glow squid rarity: %s", - "rainglow.tooltip.mode": "Determines the possible colours for entities to have.", - "rainglow.config.cannot_be_loaded_outside_world": "Rainglow config cannot be edited through the GUI before loading a world!", + "rainglow.tooltip.entity_toggle": "Enable or disable rainbow colours for this entity.", + "rainglow.config.allay_rarity.value": "Allay rarity: %s", + "rainglow.config.glow_squid_rarity.value": "Glow squid rarity: %s", "rainglow.mode.rainbow": "Rainbow", "rainglow.mode.all_colours": "All Colours", "rainglow.mode.monochrome": "Monochrome", From 8f0624358aafca14510a99ce60c487353bc03c5b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 16 May 2024 16:46:24 -0500 Subject: [PATCH 13/40] screen work 3: custom mode works now --- .../rainglow/config/CustomModeScreen.java | 33 +++++++++++++++---- .../rainglow/config/DeferredSaveOption.java | 6 ++-- .../resources/assets/rainglow/lang/en_us.json | 2 ++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 6a32b05..ca76712 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -6,13 +6,13 @@ import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.GameOptionsScreen; import net.minecraft.client.gui.widget.button.ButtonWidget; -import net.minecraft.client.gui.widget.layout.GridWidget; import net.minecraft.client.gui.widget.layout.HeaderFooterLayoutWidget; -import net.minecraft.client.gui.widget.layout.LayoutSettings; import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; import net.minecraft.client.gui.widget.list.ButtonListWidget; import net.minecraft.client.gui.widget.text.TextWidget; import net.minecraft.client.option.Option; +import net.minecraft.client.toast.SystemToast; +import net.minecraft.client.toast.Toast; import net.minecraft.text.CommonTexts; import net.minecraft.text.Text; @@ -28,8 +28,20 @@ public class CustomModeScreen extends GameOptionsScreen { public CustomModeScreen(Screen parent) { super(parent, MinecraftClient.getInstance().options, TITLE); this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { - this.save(); - this.closeScreen(); + boolean hasColourSelected = false; + for (DeferredSaveOption option : this.options) { + if (option.deferredValue) { + hasColourSelected = true; + break; + } + } + + if (!hasColourSelected) { + sendNoColoursToast(); + } else { + this.save(); + this.closeScreen(); + } }).build(); this.saveButton.active = false; } @@ -44,9 +56,7 @@ private void createColourToggles() { Rainglow.CONFIG.getCustom().contains(colour), enabled -> { if (enabled) { - Rainglow.CONFIG.getCustom().add(colour); - } else { - Rainglow.CONFIG.getCustom().remove(colour); + Rainglow.CONFIG.customColours.value().add(colour.getId()); } }, enabled -> this.saveButton.active = true @@ -55,9 +65,13 @@ private void createColourToggles() { } private void save() { + Rainglow.CONFIG.customColours.value().clear(); + for (DeferredSaveOption option : this.options) { option.save(); } + + Rainglow.CONFIG.save(); } @Override @@ -76,4 +90,9 @@ public void init() { headerFooterWidget.visitWidgets(this::addDrawableSelectableElement); headerFooterWidget.arrangeElements(); } + + private static void sendNoColoursToast() { + Toast toast = new SystemToast(SystemToast.Id.PACK_LOAD_FAILURE, Rainglow.translatableText("config.no_custom_colours"), Rainglow.translatableText("config.no_custom_colours_description")); + MinecraftClient.getInstance().getToastManager().add(toast); + } } diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index ab9c2cd..2bca295 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -11,7 +11,7 @@ public class DeferredSaveOption extends Option { public T deferredValue; - private Consumer clickCallback; + private final Consumer clickCallback; public DeferredSaveOption(String key, TooltipSupplier tooltipSupplier, OptionTextGetter textGetter, Option.ValueSet values, T defaultValue, Consumer updateCallback, Consumer clickCallback) { this(key, tooltipSupplier, textGetter, values, values.codec(), defaultValue, updateCallback, clickCallback); @@ -44,7 +44,7 @@ public void set(T value) { public static DeferredSaveOption createDeferredBoolean(String key, String tooltip, boolean defaultValue, Consumer updateCallback, Consumer clickCallback) { return new DeferredSaveOption<>( Rainglow.translatableTextKey(key), - tooltip != null ? Option.constantTooltip(Rainglow.translatableText("tooltip." + key)) : Option.emptyTooltip(), + tooltip != null ? Option.constantTooltip(Rainglow.translatableText(tooltip)) : Option.emptyTooltip(), (text, value) -> value ? CommonTexts.YES : CommonTexts.NO, BOOLEAN_VALUES, defaultValue, @@ -56,7 +56,7 @@ public static DeferredSaveOption createDeferredBoolean(String key, Stri public static DeferredSaveOption createDeferredRangedInt(String key, String tooltip, int defaultValue, int min, int max, Consumer updateCallback, Consumer clickCallback) { return new DeferredSaveOption<>( Rainglow.translatableTextKey(key), - tooltip != null ? Option.constantTooltip(Rainglow.translatableText("tooltip." + key)) : Option.emptyTooltip(), + tooltip != null ? Option.constantTooltip(Rainglow.translatableText(tooltip)) : Option.emptyTooltip(), (text, value) -> Rainglow.translatableText(key + ".value", value), new Option.IntRangeValueSet(min, max), Codec.intRange(min, max), diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index 7fc9254..7fe0888 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -12,6 +12,8 @@ "rainglow.config.enable_allay": "Rainbow allays", "rainglow.config.server_locked_title": "Config is locked by server", "rainglow.config.server_locked_description": "Changes applied will only be visible once you join a world without a locked config.", + "rainglow.config.no_custom_colours": "Cannot save with no colours selected!", + "rainglow.config.no_custom_colours_description": "Please select at least one colour to save the settings.", "rainglow.config.no_world": "No world loaded: only default modes are available.", "rainglow.config.unsaved_warning": "Config is not saved and changes will be lost! Leave without saving?", "rainglow.config.continue_editing": "Continue editing", From 2e36bcbe3467be865c0f9ca942f2294817c83631 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 16 May 2024 17:07:53 -0500 Subject: [PATCH 14/40] make server sync mandatory, fix weirdness with custom mode screen --- src/main/java/io/ix0rai/rainglow/Rainglow.java | 10 ++++------ .../io/ix0rai/rainglow/config/CustomModeScreen.java | 1 + .../io/ix0rai/rainglow/config/DeferredSaveOption.java | 9 +++------ .../java/io/ix0rai/rainglow/config/RainglowConfig.java | 3 --- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index c351ed4..664619f 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -52,13 +52,11 @@ public void onInitialize() { PayloadTypeRegistry.playS2C().register(RainglowNetworking.ModeSyncPayload.PACKET_ID, RainglowNetworking.ModeSyncPayload.PACKET_CODEC); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { - if (CONFIG.serverSync.value()) { - // send modes to client - RainglowNetworking.syncModes(handler.player); + // send modes to client + RainglowNetworking.syncModes(handler.player); - // send config to client - RainglowNetworking.syncConfig(handler.player); - } + // send config to client + RainglowNetworking.syncConfig(handler.player); }); } diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index ca76712..0f14d9a 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -72,6 +72,7 @@ private void save() { } Rainglow.CONFIG.save(); + Rainglow.refreshColours(); } @Override diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 2bca295..7e46a03 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -6,7 +6,6 @@ import net.minecraft.client.option.Option; import net.minecraft.text.CommonTexts; -import java.util.Objects; import java.util.function.Consumer; public class DeferredSaveOption extends Option { @@ -33,11 +32,9 @@ public void set(T value) { if (!MinecraftClient.getInstance().isRunning()) { this.deferredValue = object; } else { - if (!Objects.equals(this.value, object)) { - this.deferredValue = object; - this.clickCallback.accept(object); - // note: update callback is called on save - } + this.deferredValue = object; + this.clickCallback.accept(object); + // note: update callback is called on save } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index 737863b..b33eed8 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -22,9 +22,6 @@ public class RainglowConfig extends ReflectiveConfig { @Comment("The currently active rainglow mode, which determines the possible colours for entities to spawn with.") @Comment("If custom, will be reset to the default mode if you join a server that does not have the mode.") public final TrackedValue mode = this.value("rainbow"); - @Comment("For server owners only: whether to sync the rainglow mode and config to clients when they join to give everyone a unified experience.") - @Comment("When this is turned off, players will not be able to see the same colours as each other, and players will see different colours each time they rejoin.") - public final TrackedValue serverSync = this.value(true); @Comment("The rarity of coloured entities, with 0 making all entities vanilla and 100 making all entities coloured.") public final TrackedValue> rarities = this.createMap(100); @Comment("Toggles for disabling colours for each entity.") From 9bf79547c820200098cb56ff11353242f205822b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 May 2024 20:58:54 -0500 Subject: [PATCH 15/40] config changes - move back to root folder - use qconf 1.3.1 --- build.gradle | 4 ++-- src/main/java/io/ix0rai/rainglow/Rainglow.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 4c0a94e..6a48f21 100644 --- a/build.gradle +++ b/build.gradle @@ -27,8 +27,8 @@ dependencies { modImplementation("net.fabricmc:fabric-loader:${project.loader_version}") modImplementation("com.terraformersmc:modmenu:${project.mod_menu_version}") - implementation("folk.sisby:kaleido-config:0.3.0+1.3.0") - include("folk.sisby:kaleido-config:0.3.0+1.3.0") + implementation("folk.sisby:kaleido-config:0.3.1+1.3.1") + include("folk.sisby:kaleido-config:0.3.1+1.3.1") Set apiModules = [ "fabric-networking-api-v1", diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 664619f..4fc07ba 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -31,7 +31,7 @@ public class Rainglow implements ModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); private static final String FORMAT = "toml"; private static final ConfigEnvironment ENVIRONMENT = new ConfigEnvironment(FabricLoader.getInstance().getConfigDir(), FORMAT, TomlSerializer.INSTANCE); - public static final RainglowConfig CONFIG = RainglowConfig.create(ENVIRONMENT, MOD_ID, MOD_ID, RainglowConfig.class); + public static final RainglowConfig CONFIG = RainglowConfig.create(ENVIRONMENT, "", MOD_ID, RainglowConfig.class); public static final Gson GSON = new Gson(); private static final List COLOURS = new ArrayList<>(); From 34ff9977aa05a31e3593339628d3228cf2b35034 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 May 2024 21:02:26 -0500 Subject: [PATCH 16/40] fix actions --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cfbd041..1e133f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,11 +8,11 @@ jobs: steps: - uses: actions/checkout@v3 - - name: set up JDK 17 + - name: set up JDK 21 uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: grant execute permission for gradlew run: chmod +x gradlew - name: build with gradle @@ -21,4 +21,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: artifacts - path: build/libs/ \ No newline at end of file + path: build/libs/ From ff67c95ceb492b2baa6f96015f8c7149d696f3c2 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 May 2024 21:20:39 -0500 Subject: [PATCH 17/40] fix click callback running on init, fully remove vanilla mode --- .../java/io/ix0rai/rainglow/config/DeferredSaveOption.java | 4 +++- .../resources/assets/rainglow/custom_modes/vanilla.json | 7 ------- src/main/resources/assets/rainglow/lang/de_de.json | 1 - src/main/resources/assets/rainglow/lang/en_us.json | 1 - src/main/resources/assets/rainglow/lang/fr_fr.json | 1 - 5 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 src/main/resources/assets/rainglow/custom_modes/vanilla.json diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 7e46a03..201b7eb 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -33,7 +33,9 @@ public void set(T value) { this.deferredValue = object; } else { this.deferredValue = object; - this.clickCallback.accept(object); + if (!object.equals(this.value)) { + this.clickCallback.accept(object); + } // note: update callback is called on save } } diff --git a/src/main/resources/assets/rainglow/custom_modes/vanilla.json b/src/main/resources/assets/rainglow/custom_modes/vanilla.json deleted file mode 100644 index bbafc01..0000000 --- a/src/main/resources/assets/rainglow/custom_modes/vanilla.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "vanilla", - "textColour": "FFFFFF", - "colourIds": [ - "blue" - ] -} diff --git a/src/main/resources/assets/rainglow/lang/de_de.json b/src/main/resources/assets/rainglow/lang/de_de.json index 5e654b1..bd1c325 100644 --- a/src/main/resources/assets/rainglow/lang/de_de.json +++ b/src/main/resources/assets/rainglow/lang/de_de.json @@ -16,7 +16,6 @@ "rainglow.mode.rainbow": "Regenbogen", "rainglow.mode.all_colours": "Alle Farben", "rainglow.mode.monochrome": "Monochrom", - "rainglow.mode.vanilla": "Standard", "rainglow.mode.custom": "Benutzerdefiniert", "rainglow.mode.trans_pride": "Transgender Pride!", "rainglow.mode.lesbian_pride": "Lesbian Pride!", diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index 7fe0888..e55c270 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -25,7 +25,6 @@ "rainglow.mode.rainbow": "Rainbow", "rainglow.mode.all_colours": "All Colours", "rainglow.mode.monochrome": "Monochrome", - "rainglow.mode.vanilla": "Vanilla", "rainglow.mode.custom": "Custom", "rainglow.mode.trans_pride": "Transgender Pride!", "rainglow.mode.lesbian_pride": "Lesbian Pride!", diff --git a/src/main/resources/assets/rainglow/lang/fr_fr.json b/src/main/resources/assets/rainglow/lang/fr_fr.json index 3a630ba..6e9658d 100644 --- a/src/main/resources/assets/rainglow/lang/fr_fr.json +++ b/src/main/resources/assets/rainglow/lang/fr_fr.json @@ -16,7 +16,6 @@ "rainglow.mode.rainbow": "Arc-en-ciel", "rainglow.mode.all_colours": "Toutes les Couleurs", "rainglow.mode.monochrome": "Monochrome", - "rainglow.mode.vanilla": "Vanille", "rainglow.mode.custom": "Personnalisé", "rainglow.mode.trans_pride": "Fierté Transgenre !", "rainglow.mode.lesbian_pride": "Fierté Lesbienne !", From 67462012c0ff3260c8dbf34bf2dce6855051c948 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 May 2024 21:21:24 -0500 Subject: [PATCH 18/40] remove garbage from non-english lang files --- src/main/resources/assets/rainglow/lang/de_de.json | 7 +------ src/main/resources/assets/rainglow/lang/fr_fr.json | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/resources/assets/rainglow/lang/de_de.json b/src/main/resources/assets/rainglow/lang/de_de.json index bd1c325..2cf0cc1 100644 --- a/src/main/resources/assets/rainglow/lang/de_de.json +++ b/src/main/resources/assets/rainglow/lang/de_de.json @@ -11,8 +11,6 @@ "rainglow.config.enable_slime": "Regenbogenfarbener Schleim", "rainglow.config.enable_allay": "Regenbogenfarbene Hilfsgeister", "rainglow.config.server_locked_title": "Konfiguration vom Server gesperrt", - "rainglow.tooltip.mode": "Der Modus bestimmt, welche Farben gerade aktiviert sind", - "rainglow.config.cannot_be_loaded_outside_world": "Die Rainglow Konfiguration kann nicht außerhalb einer Welt geladen werden!", "rainglow.mode.rainbow": "Regenbogen", "rainglow.mode.all_colours": "Alle Farben", "rainglow.mode.monochrome": "Monochrom", @@ -43,8 +41,5 @@ "rainglow.colour.lime": "Hellgrün", "rainglow.colour.magenta": "Magenta", "rainglow.colour.indigo": "Indigo", - "rainglow.value.slime_rarity": "Slime rarity: %s", - "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", - "rainglow.value.allay_rarity": "Allay rarity: %s", - "rainglow.value.glow_squid_rarity": "Glow squid rarity: %s" + "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour." } diff --git a/src/main/resources/assets/rainglow/lang/fr_fr.json b/src/main/resources/assets/rainglow/lang/fr_fr.json index 6e9658d..82d6615 100644 --- a/src/main/resources/assets/rainglow/lang/fr_fr.json +++ b/src/main/resources/assets/rainglow/lang/fr_fr.json @@ -11,8 +11,6 @@ "rainglow.config.enable_slime": "Slimes arc-en-ciel", "rainglow.config.enable_allay": "Allays arc-en-ciel", "rainglow.config.server_locked_title": "La configuration est verrouillée par le serveur", - "rainglow.tooltip.mode": "Le mode Rainglow dicte quelles couleurs sont actuellement disponibles pour les poulpes", - "rainglow.config.cannot_be_loaded_outside_world": "La configuration de Rainglow ne peut pas être modifiée par l'interface utilisateur tant qu'un monde n'est pas chargé !", "rainglow.mode.rainbow": "Arc-en-ciel", "rainglow.mode.all_colours": "Toutes les Couleurs", "rainglow.mode.monochrome": "Monochrome", From dcf8e0e215e93f2160c0459f88239730f4cc8f6c Mon Sep 17 00:00:00 2001 From: orifu Date: Sun, 19 May 2024 17:19:33 +0100 Subject: [PATCH 19/40] add dedicated particle textures for slimes --- .../io/ix0rai/rainglow/data/RainglowColour.java | 12 ++++++------ .../minecraft/models/item/amethyst_shard.json | 7 +++++++ .../assets/minecraft/models/item/black_dye.json | 7 +++++++ .../assets/minecraft/models/item/blue_dye.json | 7 +++++++ .../assets/minecraft/models/item/brown_dye.json | 7 +++++++ .../assets/minecraft/models/item/cyan_dye.json | 7 +++++++ .../assets/minecraft/models/item/gray_dye.json | 7 +++++++ .../assets/minecraft/models/item/green_dye.json | 7 +++++++ .../minecraft/models/item/light_blue_dye.json | 7 +++++++ .../minecraft/models/item/light_gray_dye.json | 7 +++++++ .../assets/minecraft/models/item/lime_dye.json | 7 +++++++ .../assets/minecraft/models/item/magenta_dye.json | 7 +++++++ .../assets/minecraft/models/item/orange_dye.json | 7 +++++++ .../assets/minecraft/models/item/pink_dye.json | 7 +++++++ .../assets/minecraft/models/item/purple_dye.json | 7 +++++++ .../assets/minecraft/models/item/red_dye.json | 7 +++++++ .../assets/minecraft/models/item/white_dye.json | 7 +++++++ .../assets/minecraft/models/item/yellow_dye.json | 7 +++++++ .../textures/block/slime_block/black.png | Bin 0 -> 199 bytes .../minecraft/textures/block/slime_block/blue.png | Bin 0 -> 349 bytes .../textures/block/slime_block/brown.png | Bin 0 -> 342 bytes .../minecraft/textures/block/slime_block/cyan.png | Bin 0 -> 342 bytes .../minecraft/textures/block/slime_block/gray.png | Bin 0 -> 335 bytes .../textures/block/slime_block/green.png | Bin 0 -> 317 bytes .../textures/block/slime_block/indigo.png | Bin 0 -> 360 bytes .../textures/block/slime_block/light_blue.png | Bin 0 -> 354 bytes .../textures/block/slime_block/light_gray.png | Bin 0 -> 334 bytes .../textures/block/slime_block/magenta.png | Bin 0 -> 343 bytes .../textures/block/slime_block/orange.png | Bin 0 -> 375 bytes .../minecraft/textures/block/slime_block/pink.png | Bin 0 -> 342 bytes .../textures/block/slime_block/purple.png | Bin 0 -> 367 bytes .../minecraft/textures/block/slime_block/red.png | Bin 0 -> 340 bytes .../textures/block/slime_block/white.png | Bin 0 -> 201 bytes .../textures/block/slime_block/yellow.png | Bin 0 -> 375 bytes 34 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/assets/minecraft/models/item/amethyst_shard.json create mode 100644 src/main/resources/assets/minecraft/models/item/black_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/blue_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/brown_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/cyan_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/gray_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/green_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/light_blue_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/light_gray_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/lime_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/magenta_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/orange_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/pink_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/purple_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/red_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/white_dye.json create mode 100644 src/main/resources/assets/minecraft/models/item/yellow_dye.json create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/black.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/blue.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/brown.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/cyan.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/gray.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/green.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/indigo.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/light_blue.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/light_gray.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/magenta.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/orange.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/pink.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/purple.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/red.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/white.png create mode 100644 src/main/resources/assets/minecraft/textures/block/slime_block/yellow.png diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java index 15de842..d570ab1 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java @@ -12,15 +12,15 @@ public enum RainglowColour { BLACK("black", new RGB(0.0F, 0.0F, 0.0F), new RGB(0.0F, 0.0F, 0.0F), new RGB(0, 0, 0), Items.BLACK_DYE), BLUE("blue", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.BLUE_DYE), - BROWN("brown", new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(149, 59, 35), Items.BROWN_DYE), // todo particles - CYAN("cyan", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.CYAN_DYE), // todo particles + BROWN("brown", new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(149, 59, 35), Items.BROWN_DYE), + CYAN("cyan", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.CYAN_DYE), GRAY("gray", new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.GRAY_DYE), GREEN("green", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.GREEN_DYE), INDIGO("indigo", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 1.0F), new RGB(0, 0, 200), Items.AMETHYST_SHARD), - LIGHT_BLUE("light_blue", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.LIGHT_BLUE_DYE), // todo particles - LIGHT_GRAY("light_gray", new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.LIGHT_GRAY_DYE), // todo particles - LIME("lime", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.LIME_DYE), // todo particles - MAGENTA("magenta", new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.MAGENTA_DYE), // todo particles + LIGHT_BLUE("light_blue", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 0.4F, 0.4F), new RGB(204, 31, 102), Items.LIGHT_BLUE_DYE), + LIGHT_GRAY("light_gray", new RGB(0.6F, 0.6F, 0.6F), new RGB(0.4F, 0.4F, 0.4F), new RGB(100, 100, 100), Items.LIGHT_GRAY_DYE), + LIME("lime", new RGB(0.6F, 1.0F, 0.8F), new RGB(0.08F, 1.0F, 0.4F), new RGB(0, 200, 0), Items.LIME_DYE), + MAGENTA("magenta", new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.MAGENTA_DYE), ORANGE("orange", new RGB(1.0F, 0.5F, 0.0F), new RGB(1.0F, 0.4F, 0.4F), new RGB(200, 0, 0), Items.ORANGE_DYE), PINK("pink", new RGB(0.6F, 0F, 0.5F), new RGB(1.0F, 0.1F, 1.0F), new RGB(200, 0, 0), Items.PINK_DYE), PURPLE("purple", new RGB(0.3F, 0F, 0.25F), new RGB(0.5F, 0.05F, 0.5F), new RGB(200, 0, 100), Items.PURPLE_DYE), diff --git a/src/main/resources/assets/minecraft/models/item/amethyst_shard.json b/src/main/resources/assets/minecraft/models/item/amethyst_shard.json new file mode 100644 index 0000000..ac79550 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/amethyst_shard.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/amethyst_shard", + "particle": "minecraft:block/slime_block/indigo" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/black_dye.json b/src/main/resources/assets/minecraft/models/item/black_dye.json new file mode 100644 index 0000000..a98d66a --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/black_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/black_dye", + "particle": "minecraft:block/slime_block/black" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/blue_dye.json b/src/main/resources/assets/minecraft/models/item/blue_dye.json new file mode 100644 index 0000000..bd67d1f --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/blue_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/blue_dye", + "particle": "minecraft:block/slime_block/blue" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/brown_dye.json b/src/main/resources/assets/minecraft/models/item/brown_dye.json new file mode 100644 index 0000000..4437d6b --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/brown_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/brown_dye", + "particle": "minecraft:block/slime_block/brown" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/cyan_dye.json b/src/main/resources/assets/minecraft/models/item/cyan_dye.json new file mode 100644 index 0000000..2e7c220 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/cyan_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/cyan_dye", + "particle": "minecraft:block/slime_block/cyan" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/gray_dye.json b/src/main/resources/assets/minecraft/models/item/gray_dye.json new file mode 100644 index 0000000..0447947 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/gray_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/gray_dye", + "particle": "minecraft:block/slime_block/gray" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/green_dye.json b/src/main/resources/assets/minecraft/models/item/green_dye.json new file mode 100644 index 0000000..5e0915e --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/green_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/green_dye", + "particle": "minecraft:block/slime_block/green" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/light_blue_dye.json b/src/main/resources/assets/minecraft/models/item/light_blue_dye.json new file mode 100644 index 0000000..76cf813 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/light_blue_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/light_blue_dye", + "particle": "minecraft:block/slime_block/light_blue" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/light_gray_dye.json b/src/main/resources/assets/minecraft/models/item/light_gray_dye.json new file mode 100644 index 0000000..f3ea813 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/light_gray_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/light_gray_dye", + "particle": "minecraft:block/slime_block/light_gray" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/lime_dye.json b/src/main/resources/assets/minecraft/models/item/lime_dye.json new file mode 100644 index 0000000..f94c7ef --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/lime_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/lime_dye", + "particle": "minecraft:block/slime_block" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/magenta_dye.json b/src/main/resources/assets/minecraft/models/item/magenta_dye.json new file mode 100644 index 0000000..021065d --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/magenta_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/magenta_dye", + "particle": "minecraft:block/slime_block/magenta" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/orange_dye.json b/src/main/resources/assets/minecraft/models/item/orange_dye.json new file mode 100644 index 0000000..2fa13e4 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/orange_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/orange_dye", + "particle": "minecraft:block/slime_block/orange" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/pink_dye.json b/src/main/resources/assets/minecraft/models/item/pink_dye.json new file mode 100644 index 0000000..4bf3791 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/pink_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/pink_dye", + "particle": "minecraft:block/slime_block/pink" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/purple_dye.json b/src/main/resources/assets/minecraft/models/item/purple_dye.json new file mode 100644 index 0000000..9c1f87d --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/purple_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/purple_dye", + "particle": "minecraft:block/slime_block/purple" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/red_dye.json b/src/main/resources/assets/minecraft/models/item/red_dye.json new file mode 100644 index 0000000..f976836 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/red_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/red_dye", + "particle": "minecraft:block/slime_block/red" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/white_dye.json b/src/main/resources/assets/minecraft/models/item/white_dye.json new file mode 100644 index 0000000..cbd98a7 --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/white_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/white_dye", + "particle": "minecraft:block/slime_block/white" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/models/item/yellow_dye.json b/src/main/resources/assets/minecraft/models/item/yellow_dye.json new file mode 100644 index 0000000..f64fb1b --- /dev/null +++ b/src/main/resources/assets/minecraft/models/item/yellow_dye.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/yellow_dye", + "particle": "minecraft:block/slime_block/yellow" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/black.png b/src/main/resources/assets/minecraft/textures/block/slime_block/black.png new file mode 100644 index 0000000000000000000000000000000000000000..19544b2dfb31616d71c0a2e7db054c84f65fdfd5 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`eV#6kAr*6`PLAa}puo{=f70*h z$1;oaTh)cGJlfKEQb$|U-s}NuL3!1CmOHWu4?oE+aBYfqF$!+{BkSJDm?L)P+v5;D zohHkPuUdJG;BQ4{vZa`5<`v47_x`7}G^yyfwx{|sw?Cb!@H%#s3h8H1;*pUXO@geCyYN>5(^ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/blue.png b/src/main/resources/assets/minecraft/textures/block/slime_block/blue.png new file mode 100644 index 0000000000000000000000000000000000000000..49266fc007d2a43b97f33efd811d033338db4444 GIT binary patch literal 349 zcmV-j0iyniP)HFvJ7eG}> zDFKjkMpXf5ng)Z95jinUcLX5Dni%U6DDpms5Xd=~Vs^e>Z}j~I2_XQmERWLJx*7{h zwqmME+jcC=0~yDWuKOxK=6M1jrL>}Nqj`+uNaed(vZrb+hT+CA+*Sperg>_xERUU7 z_MK8fzV(E8EiFux% z+I*gbbsTV{tqpR%T{f7Gmm;#G+FCo!&T~7v0t(gP9c+x_psrH2`{ya5YGB8itmEw7 zZL)~$1hjq_BY5vG1;hwj>*KV_$@)KpFvL;X92}`kLI^J(XrQI3wC&22zJ;?Uu>tng oy@RW@vMl%Un2r{{(#5j+15bjxF;@yFlmGw#07*qoM6N<$fzV(E8EiFux% z+I*gbbsTV{tqpR%T{f7Gmm;#G+FCo!&T~7v0t(gP9c+x_psrH2`{ya5YGB8itmEw7 zZL)~$1hjq_BY5vG1;hwj>*KV_$@)KpFvL;X92}`kLI^J(XrQI3wC&22zJ;?Uu>tng oy@RW@vMl%Un2r{{(#5j+1CzbGLNZRfJOBUy07*qoM6N<$f+dWh#{d8T literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/gray.png b/src/main/resources/assets/minecraft/textures/block/slime_block/gray.png new file mode 100644 index 0000000000000000000000000000000000000000..623f2b05baafdda4ec657d41ff7b64218447481a GIT binary patch literal 335 zcmV-V0kHmwP)+RjqIU^={mj|p7VHL8C9yhJn&{5YF;oZq`o7Li3j z?}s@;jPX%GoUp2TTjl2cpHiCQ7;P?&G$tvfe;-(&r)jkP%8b58@FwvA{?)xhP*rNJ h^D$j5{G^L#^$Ut4yFtndxK{uG002ovPDHLkV1ljgl5_w7 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/green.png b/src/main/resources/assets/minecraft/textures/block/slime_block/green.png new file mode 100644 index 0000000000000000000000000000000000000000..70c758add15c9c0db04b9f93f24aa2ab6a10471f GIT binary patch literal 317 zcmV-D0mA-?P)6F)`i1aW3^lp0qySK9$ zgMoI)0JeZF9case04z5`C-ZgPDz|4MTXy=M*|Vw$mHybtR76@62y%UiwAy*0y-9CU zyDBd&MtzAiWZQe)4t6iSWWiCHPjzbawmiw>!7Fj#oo~eJPX@swQiK_kqA2gkWo6{E{ P00000NkvXXu0mjfSOAIn literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/indigo.png b/src/main/resources/assets/minecraft/textures/block/slime_block/indigo.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd982b694fcf316ecf53c856065ab812520e89b GIT binary patch literal 360 zcmV-u0hj)XP)428c16EY1-PG-q9{ki)&{VTg~mj-e&hI%EKutO_XNlM>IiDl{O>G|#S^?d>e zG2p!iV7|_T7yxJwEef0CDo08Q2OuJdNJ(_!N?bQJ^K~x8H2xWXISvD^ZfXGXl1pdp z8e)J*xva%P4D?+`UNUZ)Ci>659Hh%10N#6A9`toIi)osuY&X4Zsj3#|apXLXs{-wz zy=jn_yc0{mz4y54ripyK?}T-1U=I~}$@E?Kv_ZA~8Ddybov-t5cJA9*6;P-SuV8Z= z8+Dbc-9L8`RRcRtWgVySwrGf9C7|^~8KG|KrvhSxO_#sjDl6-M5h>y*ZHyN%s}zxk z4>ZxzRN8iBO5eg+lh_2`JYL-^xResdVJMGjYvE_QSXTca7Q8zIAlVN90000UHY-i7XU&CEBZQ`#WYP6wwoqfs>u#yB!{m8#u8 z4-r)ZJ5FI8XZLQCMPwzQ^+OuLd;e5GjIhgm*{rg#{%`6kiKDd19H~s2y88El23nd* z+pbLMTR3YH8(?4EE4cH#aP0f^m~sn0)5Ws-1>Z-zSBR=;X8-^I07*qoM6N<$f;O$1 Af&c&j literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/light_gray.png b/src/main/resources/assets/minecraft/textures/block/slime_block/light_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..4d389298753aa0ed2da717b8d646eb133caed7c9 GIT binary patch literal 334 zcmV-U0kQsxP)sH%s_5becw;o_}IV& zSK8Jf=ls}UyI!j5iRu{RHaqX_oC+9BhfnY^fs49E)$gCTh^B!bXY-Epd-utzdJ@q4 zVT}-Dd=wBTZ14TH%FX*Ec=a0%^p%Ii@=?p#T5?07*qoM6N<$f@7GLg#Z8m literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/magenta.png b/src/main/resources/assets/minecraft/textures/block/slime_block/magenta.png new file mode 100644 index 0000000000000000000000000000000000000000..6b24506dc9988539b1e0a33a01ad9dd047f949ff GIT binary patch literal 343 zcmV-d0jU0oP)Wp-ZMITSFM4)5S&0w;Bis^32^5lsU>PUjux z_wJKb^&p`4LmMH+cq<@I*gfx;Rd(M0BGSY$+MFC|Ohn}02NvjQ8g0Kaqwf*CNqm5R pb?*>z&dkrbJ*KmT?{x93egW3|yOwKa%HaS2002ovPDHLkV1f-ylNSI0 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/orange.png b/src/main/resources/assets/minecraft/textures/block/slime_block/orange.png new file mode 100644 index 0000000000000000000000000000000000000000..143103d345730cd586423c05d40a6c89f08e3b12 GIT binary patch literal 375 zcmV--0f_#IP)=Fls88d?lY$`-LEI1!+QaUSv(a`=OLlhV|G$|$ z{d|3V1Ngp0p5FmrIN|#i0I6zHWI1jI=OYe)vO&s56Db=co##s!PPbz5-0gR))*Br2 zWk|e^+BtRgeG67eEav-`)yo@PN1Q)wk=IYX@cse>slp^;ceQlbQrK?caV|Bn==M9h z{VppYRZWrPOI0JXKxCnhfB-G>5n0@UbyJo0QsX)X&vqIyub6)HeVa=iPIulRPRAC| zLXpFNVRM{ziJ?->{D}|~18b+S8)xC!$G&fKfMy?V7Us*x01a60FL$FX?EX_W3Ma|N z>;h(#lnqkVfk8?X$?c?_ND8DPYY-s1nO0VRfTYBD|5rH9M^@{N{!G&qe#FJ9`UPlL VrnIIOaku~g002ovPDHLkV1g2qreXj9 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/pink.png b/src/main/resources/assets/minecraft/textures/block/slime_block/pink.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1a2a83f13a10502791c7133589d818d1244386 GIT binary patch literal 342 zcmV-c0jd6pP)Zqt+k6-`W<6r9LJY7 zkH|GY(14eU6Bb)4P1 zO%{=ZfYuLv1n>Q=fEZ!RzF$^3SpSC*x;RRklOvT$2;tuc8fa-MZM!n1Z{e&-Y=C`r opWsR<>F+l?ecS z94lK@RfUHULI^`LRi)Zi^bzEAJoE8c3>)pW1)$s%1V;OIHLuh0OyaBAVQ5pZ_$M0glsz7MiTZM1)&P zwAOGrXHrW2$Ey?oVvHyHI+{h!naOt3WJ^_9?Ayk^ZKnduvixb#TDyp)-!Vq!c?RJ7 z(8k6(28^_|!7Zh?4W{u@L{3y|t(Vz3wsR_=P#r$O#yAFbm8#u8hlr|y9cQwRvwOG6 zB61SY`k{~Dy?-kpM%Y&=msL*I{~?4fj?!juq%sL1{QE!yEls6uSElqWoHdCJu&?eD mTq%Y7y7tF3TKJtVment{47*DI)XA3s0000ogBz_K!L;g{t3Gs z|KFAg{ktc4#M}{5N#ePih_&#(#H9jE9`cJ8Arq`2N9apvuQ#YLI zzq)dR$1|r>+)I~iS>TZ!bM#&8rfzZmYYQgrI-Jf`UwFsqdH2(arI*TuwRGo5%}A8x z{J0_HVRYE+q=pZtAl8D~GvU=|HnGzsW722WQ%mvv4FO#mpi BQX2pO literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/minecraft/textures/block/slime_block/yellow.png b/src/main/resources/assets/minecraft/textures/block/slime_block/yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..48465e553aa24bd791870864a7aa1b6af720b08e GIT binary patch literal 375 zcmV--0f_#IP)W;GM&}T!(iK2mq|=nsJ&knVgU3$YH<6uId_q zImX;sx+WDNKNhmCk9>U?<`}W3OW^Z;oiDBfK$6-v6B^Q8aJmF4-7P&XrHU8FbL4oA z1wq@G Date: Mon, 20 May 2024 21:54:39 -0500 Subject: [PATCH 20/40] fix slime particle quantity --- .../io/ix0rai/rainglow/mixin/SlimeEntityMixin.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index 0a32703..b800b15 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -71,20 +71,21 @@ public boolean spawnWithParentColour(World instance, Entity entity) { */ @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;addParticle(Lnet/minecraft/particle/ParticleEffect;DDDDDD)V"), slice = @Slice( - from = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SlimeEntity;getSize()I"), + from = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SlimeEntity;getDimensions(Lnet/minecraft/entity/EntityPose;)Lnet/minecraft/entity/EntityDimensions;"), to = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SlimeEntity;playSound(Lnet/minecraft/sound/SoundEvent;FF)V") ) ) public void tick(CallbackInfo ci) { - int size = this.getSize(); + float size = this.getDimensions(this.getPose()).width(); String colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)).getId(); int index = Rainglow.getColourIndex(colour); - for(int j = 0; j < size * 2; j ++) { + for(int j = 0; j < size / 4; j ++) { float f = this.random.nextFloat() * 6.2831855F; float g = this.random.nextFloat() * 0.5F + 0.5F; - float h = MathHelper.sin(f) * (float)size * 0.5F * g; - float k = MathHelper.cos(f) * (float)size * 0.5F * g; + float h = MathHelper.sin(f) * size * g; + float k = MathHelper.cos(f) * size * g; + // note: y velocity of 100 is a magic value this.getWorld().addParticle(this.getParticles(), this.getX() + (double)h, this.getY(), this.getZ() + (double)k, index, 100.0, 0.0); } } From c2cf98c967c1efff21633b4aaa304fbe46001f18 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Mon, 20 May 2024 21:57:21 -0500 Subject: [PATCH 21/40] fix some config issues --- .../java/io/ix0rai/rainglow/config/CustomModeScreen.java | 6 +++--- .../io/ix0rai/rainglow/config/RainglowConfigScreen.java | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 0f14d9a..d0d112d 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -53,10 +53,10 @@ private void createColourToggles() { this.options.add(DeferredSaveOption.createDeferredBoolean( "colour." + colour.getId(), null, - Rainglow.CONFIG.getCustom().contains(colour), + Rainglow.CONFIG.customColours.getRealValue().contains(colour.getId()), enabled -> { if (enabled) { - Rainglow.CONFIG.customColours.value().add(colour.getId()); + Rainglow.CONFIG.customColours.getRealValue().add(colour.getId()); } }, enabled -> this.saveButton.active = true @@ -65,7 +65,7 @@ private void createColourToggles() { } private void save() { - Rainglow.CONFIG.customColours.value().clear(); + Rainglow.CONFIG.customColours.getRealValue().clear(); for (DeferredSaveOption option : this.options) { option.save(); diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 9a5273a..5aa8b97 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -42,7 +42,7 @@ public class RainglowConfigScreen extends Screen { public RainglowConfigScreen(@Nullable Screen parent) { super(TITLE); this.parent = parent; - this.mode = RainglowMode.get(Rainglow.CONFIG.mode.value()); + this.mode = RainglowMode.get(Rainglow.CONFIG.mode.getRealValue()); this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { this.save(); this.closeScreen(true); @@ -109,7 +109,7 @@ private DeferredSaveOption createEntityToggle(RainglowEntity entity) { return toggles.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredBoolean( "config.enable_" + e.getId(), "tooltip.entity_toggle", - Rainglow.CONFIG.isEntityEnabled(e), + Rainglow.CONFIG.toggles.getRealValue().get(e.getId()), enabled -> Rainglow.CONFIG.setEntityEnabled(e, enabled), enabled -> this.saveButton.active = true )); @@ -119,7 +119,7 @@ private DeferredSaveOption createColourRaritySlider(RainglowEntity enti return sliders.computeIfAbsent(entity, e -> DeferredSaveOption.createDeferredRangedInt( "config." + e.getId() + "_rarity", "tooltip.rarity", - Rainglow.CONFIG.getRarity(e), + Rainglow.CONFIG.rarities.getRealValue().get(e.getId()), 0, 100, rarity -> Rainglow.CONFIG.setRarity(e, rarity), @@ -156,6 +156,7 @@ private void save() { } Rainglow.CONFIG.mode.setValue(this.mode.getId()); + Rainglow.setMode(RainglowMode.get(this.mode.getId())); } private Tooltip createColourListLabel(RainglowMode mode) { From 51c8eff2a2d32428368b00cab72840ed1c2df0b4 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Mon, 20 May 2024 22:04:05 -0500 Subject: [PATCH 22/40] slightly better slime particles --- src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index b800b15..1f187ff 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -80,13 +80,13 @@ public void tick(CallbackInfo ci) { String colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)).getId(); int index = Rainglow.getColourIndex(colour); - for(int j = 0; j < size / 4; j ++) { + for (int j = 0; j < size / 2; j ++) { float f = this.random.nextFloat() * 6.2831855F; float g = this.random.nextFloat() * 0.5F + 0.5F; float h = MathHelper.sin(f) * size * g; float k = MathHelper.cos(f) * size * g; // note: y velocity of 100 is a magic value - this.getWorld().addParticle(this.getParticles(), this.getX() + (double)h, this.getY(), this.getZ() + (double)k, index, 100.0, 0.0); + this.getWorld().addParticle(this.getParticles(), this.getX() + (double) h, this.getY(), this.getZ() + (double) k, index, 100.0, 0.0); } } From e6e3513a50392371047015d0408ab4301b2890bb Mon Sep 17 00:00:00 2001 From: ix0rai Date: Mon, 20 May 2024 22:25:23 -0500 Subject: [PATCH 23/40] fix some config stuff --- .../rainglow/client/RainglowClient.java | 39 ++++++++++++------- .../rainglow/config/RainglowConfig.java | 9 +++++ .../rainglow/config/RainglowConfigScreen.java | 4 +- .../rainglow/data/RainglowNetworking.java | 8 ++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java index 985ce8c..1907cd2 100644 --- a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java +++ b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java @@ -2,6 +2,7 @@ import folk.sisby.kaleido.lib.quiltconfig.api.values.TrackedValue; import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueList; +import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueMap; import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowMode; @@ -26,25 +27,33 @@ public class RainglowClient implements ClientModInitializer { public void onInitializeClient() { ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ConfigSyncPayload.PACKET_ID, (payload, context) -> { MinecraftClient client = context.client(); - client.execute(() -> { - // custom must be set before mode so that if the server sends a custom mode it is set correctly - // otherwise the client's custom would be used - ValueList list = ValueList.create("", payload.customMode().stream().map(RainglowColour::getId).toArray(String[]::new)); - Rainglow.CONFIG.customColours.setOverride(list); - Rainglow.CONFIG.mode.setOverride(payload.currentMode()); + if (!client.isIntegratedServerRunning()) { + client.execute(() -> { + // custom must be set before mode so that if the server sends a custom mode it is set correctly + // otherwise the client's custom would be used + ValueList customColours = ValueList.create("", payload.customMode().stream().map(RainglowColour::getId).toArray(String[]::new)); + Rainglow.CONFIG.customColours.setOverride(customColours); + Rainglow.CONFIG.mode.setOverride(payload.currentMode()); - // todo override toggles + var rarities = ValueMap.builder(0); + for (var entry : payload.rarities().entrySet()) { + rarities.put(entry.getKey().getId(), entry.getValue()); + } + Rainglow.CONFIG.rarities.setOverride(rarities.build()); - for (var entry : payload.enabledMobs().entrySet()) { - Rainglow.CONFIG.setEntityEnabled(entry.getKey(), entry.getValue()); - } + var toggles = ValueMap.builder(true); + for (var entry : payload.enabledMobs().entrySet()) { + toggles.put(entry.getKey().getId(), entry.getValue()); + } + Rainglow.CONFIG.toggles.setOverride(toggles.build()); - // lock the config from reloading on resource reload - Rainglow.CONFIG.setEditLocked(true); + // lock the config from reloading on resource reload + Rainglow.CONFIG.setEditLocked(true); - // log - Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); - }); + // log + Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); + }); + } }); ClientPlayNetworking.registerGlobalReceiver(RainglowNetworking.ModeSyncPayload.PACKET_ID, (payload, context) -> { diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index b33eed8..08948eb 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -49,6 +49,15 @@ public Map getToggles() { return map; } + public Map getRarities() { + Map map = new HashMap<>(); + for (RainglowEntity entity : RainglowEntity.values()) { + map.put(entity, this.getRarity(entity)); + } + + return map; + } + public boolean isEntityEnabled(RainglowEntity entity) { return this.toggles.value().get(entity.getId()); } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 5aa8b97..5c01b5b 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -110,7 +110,7 @@ private DeferredSaveOption createEntityToggle(RainglowEntity entity) { "config.enable_" + e.getId(), "tooltip.entity_toggle", Rainglow.CONFIG.toggles.getRealValue().get(e.getId()), - enabled -> Rainglow.CONFIG.setEntityEnabled(e, enabled), + enabled -> Rainglow.CONFIG.toggles.getRealValue().put(e.getId(), enabled), enabled -> this.saveButton.active = true )); } @@ -122,7 +122,7 @@ private DeferredSaveOption createColourRaritySlider(RainglowEntity enti Rainglow.CONFIG.rarities.getRealValue().get(e.getId()), 0, 100, - rarity -> Rainglow.CONFIG.setRarity(e, rarity), + rarity -> Rainglow.CONFIG.rarities.getRealValue().put(e.getId(), rarity), rarity -> this.saveButton.active = true )); } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java index 6c972b4..c0b17ae 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowNetworking.java @@ -16,10 +16,10 @@ public class RainglowNetworking { public static void syncConfig(ServerPlayerEntity player) { // note: client does not need to know if server sync is enabled or not // they already know that it is enabled because they are receiving this packet - ServerPlayNetworking.send(player, new ConfigSyncPayload(Rainglow.CONFIG.mode.value(), Rainglow.CONFIG.getCustom(), Rainglow.CONFIG.getToggles())); + ServerPlayNetworking.send(player, new ConfigSyncPayload(Rainglow.CONFIG.mode.value(), Rainglow.CONFIG.getCustom(), Rainglow.CONFIG.getToggles(), Rainglow.CONFIG.getRarities())); } - public record ConfigSyncPayload(String currentMode, List customMode, Map enabledMobs) implements CustomPayload { + public record ConfigSyncPayload(String currentMode, List customMode, Map enabledMobs, Map rarities) implements CustomPayload { public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(Rainglow.id("config_sync")); public static final PacketCodec PACKET_CODEC = PacketCodec.create(ConfigSyncPayload::write, ConfigSyncPayload::read); @@ -27,13 +27,15 @@ public void write(RegistryByteBuf buf) { buf.writeString(this.currentMode); buf.writeCollection(this.customMode, RainglowColour::write); buf.writeMap(this.enabledMobs, RainglowEntity::write, PacketByteBuf::writeBoolean); + buf.writeMap(this.rarities, RainglowEntity::write, PacketByteBuf::writeVarInt); } public static ConfigSyncPayload read(RegistryByteBuf buf) { return new ConfigSyncPayload( buf.readString(), buf.readList(RainglowColour::read), - buf.readMap(RainglowEntity::read, PacketByteBuf::readBoolean) + buf.readMap(RainglowEntity::read, PacketByteBuf::readBoolean), + buf.readMap(RainglowEntity::read, PacketByteBuf::readVarInt) ); } From 5b83400b68edc1f419b9d8acd8f2634e0f327a1e Mon Sep 17 00:00:00 2001 From: ix0rai Date: Mon, 20 May 2024 23:45:02 -0500 Subject: [PATCH 24/40] misc fixes --- .../java/io/ix0rai/rainglow/Rainglow.java | 8 ++++-- .../ix0rai/rainglow/data/RainglowEntity.java | 14 +++++++--- .../ix0rai/rainglow/mixin/MobEntityMixin.java | 27 ++++++++++--------- .../client/AllayEntityRendererMixin.java | 1 - .../mixin/client/SlimeParticleMixin.java | 2 +- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 4fc07ba..050fb4f 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -123,7 +123,11 @@ public static RainglowColour.RGB getPassiveParticleRGB(int index, RandomGenerato return random.nextBoolean() ? colour.getPassiveParticleRgb() : colour.getAltPassiveParticleRgb(); } - public static Item getItem(int index) { + public static Item getItem(RainglowEntity entity, int index) { + if (index == -1) { + return entity.getDefaultColour().getItem(); + } + return COLOURS.get(index).getItem(); } @@ -164,7 +168,7 @@ public static TrackedData getTrackedColourData(RainglowEntity entityType public static String getColour(RainglowEntity entityType, DataTracker tracker, RandomGenerator random) { // generate random colour if the squid's colour isn't currently loaded String colour = tracker.get(getTrackedColourData(entityType)); - if (colourUnloaded(colour)) { + if (colourUnloaded(colour) && !colour.equals(entityType.getDefaultColour().getId())) { // Use last generated colour if not null else generate a new colour tracker.set(getTrackedColourData(entityType), generateRandomColourId(random)); colour = tracker.get(getTrackedColourData(entityType)); diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 76039b8..3f87647 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -7,9 +7,9 @@ import java.util.HashMap; public enum RainglowEntity { - GLOW_SQUID("glow_squid"), - ALLAY("allay"), - SLIME("slime"); + GLOW_SQUID("glow_squid", RainglowColour.BLUE), + ALLAY("allay", RainglowColour.BLUE), + SLIME("slime", RainglowColour.LIME); private static final HashMap BY_ID = new HashMap<>(); static { @@ -17,15 +17,21 @@ public enum RainglowEntity { } private final String id; + private final RainglowColour defaultColour; - RainglowEntity(String id) { + RainglowEntity(String id, RainglowColour defaultColour) { this.id = id; + this.defaultColour = defaultColour; } public String getId() { return this.id; } + public RainglowColour getDefaultColour() { + return this.defaultColour; + } + public static RainglowEntity read(PacketByteBuf buf) { return get(buf.readString()); } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java index 6ab5ea5..d41e488 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java @@ -17,7 +17,6 @@ import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.entity.passive.AllayEntity; import net.minecraft.entity.passive.GlowSquidEntity; -import net.minecraft.util.random.RandomGenerator; import net.minecraft.world.LocalDifficulty; import net.minecraft.world.ServerWorldAccess; import net.minecraft.world.World; @@ -38,23 +37,27 @@ protected MobEntityMixin(EntityType entityType, World world @Inject(method = "initialize", at = @At("RETURN"), cancellable = true) public void initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @Nullable EntityData entityData, CallbackInfoReturnable cir) { if ((Object) this instanceof GlowSquidEntity glowSquid) { - String colour = Rainglow.generateRandomColourId(this.getRandom()); - ((GlowSquidVariantProvider) glowSquid).setVariant(getColourOrDefault(this.random, RainglowColour.BLUE, colour)); - cir.setReturnValue(new GlowSquidEntityData(getColourOrDefault(this.random, RainglowColour.BLUE, colour))); + RainglowColour colour = generateColour(); + ((GlowSquidVariantProvider) glowSquid).setVariant(colour); + cir.setReturnValue(new GlowSquidEntityData(colour)); } else if ((Object) this instanceof AllayEntity allay) { - String colour = Rainglow.generateRandomColourId(this.getRandom()); - ((AllayVariantProvider) allay).setVariant(getColourOrDefault(this.random, RainglowColour.BLUE, colour)); - cir.setReturnValue(new AllayEntityData(getColourOrDefault(this.random, RainglowColour.BLUE, colour))); + RainglowColour colour = generateColour(); + ((AllayVariantProvider) allay).setVariant(colour); + cir.setReturnValue(new AllayEntityData(colour)); } else if ((Object) this instanceof SlimeEntity slime) { - String colour = Rainglow.generateRandomColourId(this.getRandom()); - ((SlimeVariantProvider) slime).setVariant(getColourOrDefault(this.random, RainglowColour.LIME, colour)); - cir.setReturnValue(new SlimeEntityData(getColourOrDefault(this.random, RainglowColour.LIME, colour))); + RainglowColour colour = generateColour(); + ((SlimeVariantProvider) slime).setVariant(colour); + cir.setReturnValue(new SlimeEntityData(colour)); } } @Unique - private RainglowColour getColourOrDefault(RandomGenerator random, RainglowColour defaultColour, String randomColour) { - return random.nextInt(100) >= Rainglow.CONFIG.getRarity(this.getCurrentEntity()) ? defaultColour : RainglowColour.get(randomColour); + private RainglowColour generateColour() { + RainglowEntity entity = getCurrentEntity(); + int i = random.nextInt(100); + int rarity = Rainglow.CONFIG.getRarity(entity); + + return i >= rarity ? entity.getDefaultColour() : RainglowColour.get(Rainglow.generateRandomColourId(this.random)); } @Unique diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java index 7efc1f6..28cb0a1 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java @@ -13,7 +13,6 @@ @Mixin(AllayEntityRenderer.class) public class AllayEntityRendererMixin { - @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(AllayEntity allayEntity, CallbackInfoReturnable cir) { String colour = Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java index 6b6f56c..81de85d 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java @@ -25,7 +25,7 @@ public void createParticle(DefaultParticleType defaultParticleType, ClientWorld cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, new ItemStack(Items.SLIME_BALL))); // 99.9d and 100.1d are used to account for floating point errors } else if (h >= 99.9d && h <= 100.1d) { - ItemStack stack = Rainglow.getItem((int) g).getDefaultStack(); + ItemStack stack = Rainglow.getItem(RainglowEntity.SLIME, (int) g).getDefaultStack(); cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, stack)); } else { cir.setReturnValue(null); From 830c80e7ab23c981bba79cc6cb92ad3697d5b02c Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 May 2024 13:01:40 -0500 Subject: [PATCH 25/40] some refactors --- .../java/io/ix0rai/rainglow/Rainglow.java | 38 +++++-------------- .../rainglow/config/CustomModeScreen.java | 7 +++- .../ix0rai/rainglow/data/RainglowEntity.java | 22 ++++++++--- .../rainglow/mixin/AllayEntityMixin.java | 8 ++-- .../ix0rai/rainglow/mixin/DyeItemMixin.java | 6 +-- .../rainglow/mixin/GlowSquidEntityMixin.java | 6 +-- .../rainglow/mixin/SlimeEntityMixin.java | 8 ++-- 7 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 050fb4f..ad52844 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -35,12 +35,9 @@ public class Rainglow implements ModInitializer { public static final Gson GSON = new Gson(); private static final List COLOURS = new ArrayList<>(); - private static final Map GLOWSQUID_TEXTURES = new HashMap<>(); + private static final Map GLOW_SQUID_TEXTURES = new HashMap<>(); private static final Map ALLAY_TEXTURES = new HashMap<>(); private static final Map SLIME_TEXTURES = new HashMap<>(); - public static final TrackedData GLOW_SQUID_COLOUR = DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING); - public static final TrackedData ALLAY_COLOUR = DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING); - public static final TrackedData SLIME_COLOUR = DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING); public static final String CUSTOM_NBT_KEY = "Colour"; @@ -70,7 +67,7 @@ public static void setMode(RainglowMode mode) { LOGGER.warn("attempted to load missing mode, resetting to rainbow"); } - GLOWSQUID_TEXTURES.clear(); + GLOW_SQUID_TEXTURES.clear(); ALLAY_TEXTURES.clear(); SLIME_TEXTURES.clear(); COLOURS.clear(); @@ -85,17 +82,10 @@ public static void setMode(RainglowMode mode) { CONFIG.setInitialized(); } - public static void refreshColours() { - // we only ever need to refresh the colours of custom mode, all other sets of colours are immutable - if (CONFIG.getMode().getId().equals("custom")) { - setMode(RainglowMode.get("custom")); - } - } - private static void addColour(RainglowColour colour) { COLOURS.add(colour); - GLOWSQUID_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.GLOW_SQUID)); + GLOW_SQUID_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.GLOW_SQUID)); ALLAY_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.ALLAY)); SLIME_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.SLIME)); @@ -105,7 +95,7 @@ private static void addColour(RainglowColour colour) { } public static Identifier getTexture(RainglowEntity entityType, String colour) { - if (entityType == RainglowEntity.GLOW_SQUID) return GLOWSQUID_TEXTURES.get(colour); + if (entityType == RainglowEntity.GLOW_SQUID) return GLOW_SQUID_TEXTURES.get(colour); else if (entityType == RainglowEntity.ALLAY) return ALLAY_TEXTURES.get(colour); else return SLIME_TEXTURES.get(colour); } @@ -140,8 +130,8 @@ public static Identifier getDefaultTexture(RainglowEntity entityType) { else return RainglowColour.BLUE.getTexture(entityType); } - public static boolean colourUnloaded(String colour) { - return !COLOURS.contains(RainglowColour.get(colour)); + public static boolean colourUnloaded(RainglowEntity entityType, String colour) { + return !COLOURS.contains(RainglowColour.get(colour)) && !colour.equals(entityType.getDefaultColour().getId()); } public static String translatableTextKey(String key) { @@ -157,21 +147,13 @@ public static Text translatableText(String key) { return Text.translatable(translatableTextKey(key)); } - public static TrackedData getTrackedColourData(RainglowEntity entityType) { - return switch (entityType) { - case GLOW_SQUID -> GLOW_SQUID_COLOUR; - case ALLAY -> ALLAY_COLOUR; - case SLIME -> SLIME_COLOUR; - }; - } - public static String getColour(RainglowEntity entityType, DataTracker tracker, RandomGenerator random) { // generate random colour if the squid's colour isn't currently loaded - String colour = tracker.get(getTrackedColourData(entityType)); - if (colourUnloaded(colour) && !colour.equals(entityType.getDefaultColour().getId())) { + String colour = tracker.get(entityType.getTrackedData()); + if (colourUnloaded(entityType, colour)) { // Use last generated colour if not null else generate a new colour - tracker.set(getTrackedColourData(entityType), generateRandomColourId(random)); - colour = tracker.get(getTrackedColourData(entityType)); + tracker.set(entityType.getTrackedData(), generateRandomColourId(random)); + colour = tracker.get(entityType.getTrackedData()); } return colour; diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index d0d112d..ca8600d 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -2,6 +2,7 @@ import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; +import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.GameOptionsScreen; @@ -72,7 +73,11 @@ private void save() { } Rainglow.CONFIG.save(); - Rainglow.refreshColours(); + + // refresh colours of custom mode + if (Rainglow.CONFIG.getMode().getId().equals("custom")) { + Rainglow.setMode(RainglowMode.get("custom")); + } } @Override diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 3f87647..8b75d39 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -1,5 +1,11 @@ package io.ix0rai.rainglow.data; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.entity.mob.SlimeEntity; +import net.minecraft.entity.passive.AllayEntity; +import net.minecraft.entity.passive.GlowSquidEntity; import net.minecraft.network.PacketByteBuf; import org.jetbrains.annotations.Nullable; @@ -7,9 +13,9 @@ import java.util.HashMap; public enum RainglowEntity { - GLOW_SQUID("glow_squid", RainglowColour.BLUE), - ALLAY("allay", RainglowColour.BLUE), - SLIME("slime", RainglowColour.LIME); + GLOW_SQUID("glow_squid", RainglowColour.BLUE, DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING)), + ALLAY("allay", RainglowColour.BLUE, DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING)), + SLIME("slime", RainglowColour.LIME, DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING)); private static final HashMap BY_ID = new HashMap<>(); static { @@ -18,11 +24,13 @@ public enum RainglowEntity { private final String id; private final RainglowColour defaultColour; + private final TrackedData trackedData; - RainglowEntity(String id, RainglowColour defaultColour) { + RainglowEntity(String id, RainglowColour defaultColour, TrackedData trackedData) { this.id = id; this.defaultColour = defaultColour; - } + this.trackedData = trackedData; + } public String getId() { return this.id; @@ -32,6 +40,10 @@ public RainglowColour getDefaultColour() { return this.defaultColour; } + public TrackedData getTrackedData() { + return this.trackedData; + } + public static RainglowEntity read(PacketByteBuf buf) { return get(buf.readString()); } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java index f5a8374..c67f5ee 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java @@ -27,7 +27,7 @@ protected AllayEntityMixin(EntityType entityType, World w @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), RainglowColour.BLUE.getId()); + builder.add(RainglowEntity.ALLAY.getTrackedData(), RainglowEntity.ALLAY.getDefaultColour().getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) @@ -40,7 +40,7 @@ public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); - if (Rainglow.colourUnloaded(colour)) { + if (Rainglow.colourUnloaded(RainglowEntity.ALLAY, colour)) { colour = Rainglow.generateRandomColourId(this.getRandom()); } @@ -51,7 +51,7 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { @Redirect(method = "duplicate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) public boolean spawnWithColour(World instance, Entity entity) { RainglowColour colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom())); - entity.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), colour.getId()); + entity.getDataTracker().set(RainglowEntity.ALLAY.getTrackedData(), colour.getId()); return this.getWorld().spawnEntity(entity); } @@ -62,7 +62,7 @@ public RainglowColour getVariant() { @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), colour.getId()); + this.getDataTracker().set(RainglowEntity.ALLAY.getTrackedData(), colour.getId()); } @Unique diff --git a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java index 664d6c3..f462935 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java @@ -35,7 +35,7 @@ private void useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity } DataTracker tracker = glowSquid.getDataTracker(); - tracker.set(Rainglow.getTrackedColourData(RainglowEntity.GLOW_SQUID), colour); + tracker.set(RainglowEntity.GLOW_SQUID.getTrackedData(), colour); cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); } else if (entity instanceof AllayEntity allayEntity && allayEntity.isAlive() && !Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()).equals(colour)) { @@ -45,7 +45,7 @@ private void useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity } DataTracker tracker = allayEntity.getDataTracker(); - tracker.set(Rainglow.getTrackedColourData(RainglowEntity.ALLAY), colour); + tracker.set(RainglowEntity.ALLAY.getTrackedData(), colour); cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); } else if (entity instanceof SlimeEntity slimeEntity && slimeEntity.isAlive() && !Rainglow.getColour(RainglowEntity.SLIME, slimeEntity.getDataTracker(), slimeEntity.getRandom()).equals(colour)) { @@ -55,7 +55,7 @@ private void useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity } DataTracker tracker = slimeEntity.getDataTracker(); - tracker.set(Rainglow.getTrackedColourData(RainglowEntity.SLIME), colour); + tracker.set(RainglowEntity.SLIME.getTrackedData(), colour); cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java index 83f3139..e4e4b40 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java @@ -29,7 +29,7 @@ protected GlowSquidEntityMixin(EntityType entityType, Wor @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(Rainglow.GLOW_SQUID_COLOUR, RainglowColour.BLUE.getId()); + builder.add(RainglowEntity.GLOW_SQUID.getTrackedData(), RainglowColour.BLUE.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) @@ -43,7 +43,7 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); // if read colour does not exist in the colour map, generate the squid a new one - if (Rainglow.colourUnloaded(colour)) { + if (Rainglow.colourUnloaded(RainglowEntity.GLOW_SQUID, colour)) { colour = Rainglow.generateRandomColourId(this.getRandom()); } @@ -71,7 +71,7 @@ public RainglowColour getVariant() { @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.GLOW_SQUID), colour.getId()); + this.getDataTracker().set(RainglowEntity.GLOW_SQUID.getTrackedData(), colour.getId()); } @Mixin(SquidEntity.class) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index 1f187ff..e026234 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -35,7 +35,7 @@ protected SlimeEntityMixin(EntityType entityType, World w @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(DataTracker.Builder builder, CallbackInfo ci) { - builder.add(Rainglow.getTrackedColourData(RainglowEntity.SLIME), RainglowColour.LIME.getId()); + builder.add(RainglowEntity.SLIME.getTrackedData(), RainglowColour.LIME.getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) @@ -48,7 +48,7 @@ public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); - if (Rainglow.colourUnloaded(colour)) { + if (Rainglow.colourUnloaded(RainglowEntity.SLIME, colour)) { colour = Rainglow.generateRandomColourId(this.random); } @@ -61,7 +61,7 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { @Redirect(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) public boolean spawnWithParentColour(World instance, Entity entity) { RainglowColour colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)); - entity.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.SLIME), colour.getId()); + entity.getDataTracker().set(RainglowEntity.SLIME.getTrackedData(), colour.getId()); return this.getWorld().spawnEntity(entity); } @@ -97,6 +97,6 @@ public RainglowColour getVariant() { @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(Rainglow.getTrackedColourData(RainglowEntity.SLIME), colour.getId()); + this.getDataTracker().set(RainglowEntity.SLIME.getTrackedData(), colour.getId()); } } From 070d71e123c1fd96911d783290e5877975bc5cda Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 May 2024 18:23:36 -0500 Subject: [PATCH 26/40] more refactors --- .../java/io/ix0rai/rainglow/Rainglow.java | 44 +++---------------- .../ix0rai/rainglow/data/RainglowColour.java | 10 +++++ .../ix0rai/rainglow/data/RainglowEntity.java | 27 ++++++++++++ .../rainglow/mixin/AllayEntityMixin.java | 21 ++++----- .../rainglow/mixin/GlowSquidEntityMixin.java | 34 +++++++------- .../rainglow/mixin/SlimeEntityMixin.java | 33 ++++++-------- .../client/AllayEntityRendererMixin.java | 8 ++-- .../mixin/client/GlowParticleMixin.java | 2 +- .../client/GlowSquidEntityRendererMixin.java | 8 ++-- .../client/SlimeEntityRendererMixin.java | 9 ++-- .../mixin/client/SlimeParticleMixin.java | 2 +- .../mixin/client/SquidInkParticleMixin.java | 2 +- 12 files changed, 98 insertions(+), 102 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index ad52844..d701768 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -11,12 +11,6 @@ import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.data.TrackedData; -import net.minecraft.entity.data.TrackedDataHandlerRegistry; -import net.minecraft.entity.mob.SlimeEntity; -import net.minecraft.entity.passive.AllayEntity; -import net.minecraft.entity.passive.GlowSquidEntity; -import net.minecraft.item.Item; import net.minecraft.resource.ResourceType; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -95,41 +89,17 @@ private static void addColour(RainglowColour colour) { } public static Identifier getTexture(RainglowEntity entityType, String colour) { - if (entityType == RainglowEntity.GLOW_SQUID) return GLOW_SQUID_TEXTURES.get(colour); - else if (entityType == RainglowEntity.ALLAY) return ALLAY_TEXTURES.get(colour); - else return SLIME_TEXTURES.get(colour); - } - - public static int getColourIndex(String colour) { - return COLOURS.indexOf(RainglowColour.get(colour)); - } - - public static RainglowColour.RGB getInkRgb(int index) { - return COLOURS.get(index).getInkRgb(); - } - - public static RainglowColour.RGB getPassiveParticleRGB(int index, RandomGenerator random) { - RainglowColour colour = COLOURS.get(index); - return random.nextBoolean() ? colour.getPassiveParticleRgb() : colour.getAltPassiveParticleRgb(); - } - - public static Item getItem(RainglowEntity entity, int index) { - if (index == -1) { - return entity.getDefaultColour().getItem(); - } - - return COLOURS.get(index).getItem(); + return switch (entityType) { + case ALLAY -> ALLAY_TEXTURES.get(colour); + case SLIME -> SLIME_TEXTURES.get(colour); + case GLOW_SQUID -> GLOW_SQUID_TEXTURES.get(colour); + }; } public static String generateRandomColourId(RandomGenerator random) { return COLOURS.get(random.nextInt(COLOURS.size())).getId(); } - public static Identifier getDefaultTexture(RainglowEntity entityType) { - if (entityType == RainglowEntity.SLIME) return RainglowColour.LIME.getTexture(entityType); - else return RainglowColour.BLUE.getTexture(entityType); - } - public static boolean colourUnloaded(RainglowEntity entityType, String colour) { return !COLOURS.contains(RainglowColour.get(colour)) && !colour.equals(entityType.getDefaultColour().getId()); } @@ -147,7 +117,7 @@ public static Text translatableText(String key) { return Text.translatable(translatableTextKey(key)); } - public static String getColour(RainglowEntity entityType, DataTracker tracker, RandomGenerator random) { + public static RainglowColour getColour(RainglowEntity entityType, DataTracker tracker, RandomGenerator random) { // generate random colour if the squid's colour isn't currently loaded String colour = tracker.get(entityType.getTrackedData()); if (colourUnloaded(entityType, colour)) { @@ -156,6 +126,6 @@ public static String getColour(RainglowEntity entityType, DataTracker tracker, R colour = tracker.get(entityType.getTrackedData()); } - return colour; + return RainglowColour.get(colour); } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java index d570ab1..25a88c5 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java @@ -4,6 +4,7 @@ import net.minecraft.item.Items; import net.minecraft.network.PacketByteBuf; import net.minecraft.util.Identifier; +import net.minecraft.util.random.RandomGenerator; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -95,6 +96,15 @@ public static RainglowColour get(String id) { return BY_ID.get(id); } + public static RainglowColour.RGB getInkRgb(int index) { + return RainglowColour.values()[index].getInkRgb(); + } + + public static RainglowColour.RGB getPassiveParticleRGB(int index, RandomGenerator random) { + RainglowColour colour = RainglowColour.values()[index]; + return random.nextBoolean() ? colour.getPassiveParticleRgb() : colour.getAltPassiveParticleRgb(); + } + public record RGB(float r, float g, float b) { } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 8b75d39..a23eb22 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -1,12 +1,17 @@ package io.ix0rai.rainglow.data; +import io.ix0rai.rainglow.Rainglow; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.entity.passive.AllayEntity; import net.minecraft.entity.passive.GlowSquidEntity; +import net.minecraft.item.Item; +import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.Identifier; +import net.minecraft.util.random.RandomGenerator; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -44,6 +49,28 @@ public TrackedData getTrackedData() { return this.trackedData; } + public Identifier getDefaultTexture() { + return this.defaultColour.getTexture(this); + } + + public Item getItem(int index) { + if (index == -1) { + return this.getDefaultColour().getItem(); + } + + return RainglowColour.values()[index].getItem(); + } + + public RainglowColour readNbt(NbtCompound nbt, RandomGenerator random) { + String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); + + if (Rainglow.colourUnloaded(this, colour)) { + colour = Rainglow.generateRandomColourId(random); + } + + return RainglowColour.get(colour); + } + public static RainglowEntity read(PacketByteBuf buf) { return get(buf.readString()); } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java index c67f5ee..1a6a57d 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java @@ -20,6 +20,9 @@ @Mixin(AllayEntity.class) public abstract class AllayEntityMixin extends Entity implements AllayVariantProvider { + @Unique + private static final RainglowEntity THIS = RainglowEntity.ALLAY; + protected AllayEntityMixin(EntityType entityType, World world) { super(entityType, world); throw new UnsupportedOperationException(); @@ -27,37 +30,31 @@ protected AllayEntityMixin(EntityType entityType, World w @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(RainglowEntity.ALLAY.getTrackedData(), RainglowEntity.ALLAY.getDefaultColour().getId()); + builder.add(THIS.getTrackedData(), THIS.getDefaultColour().getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom()); - nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); + nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour.getId()); } @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); - - if (Rainglow.colourUnloaded(RainglowEntity.ALLAY, colour)) { - colour = Rainglow.generateRandomColourId(this.getRandom()); - } - - this.setVariant(RainglowColour.get(colour)); + this.setVariant(THIS.readNbt(nbt, this.getRandom())); } // triggered when an allay duplicates, to apply the same colour as parent @Redirect(method = "duplicate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) public boolean spawnWithColour(World instance, Entity entity) { - RainglowColour colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom())); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); entity.getDataTracker().set(RainglowEntity.ALLAY.getTrackedData(), colour.getId()); return this.getWorld().spawnEntity(entity); } @Override public RainglowColour getVariant() { - return RainglowColour.get(Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom())); + return Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom()); } @Override diff --git a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java index e4e4b40..98a82a0 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/GlowSquidEntityMixin.java @@ -15,6 +15,7 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -22,6 +23,9 @@ @Mixin(GlowSquidEntity.class) public abstract class GlowSquidEntityMixin extends SquidEntity implements GlowSquidVariantProvider { + @Unique + private static final RainglowEntity THIS = RainglowEntity.GLOW_SQUID; + protected GlowSquidEntityMixin(EntityType entityType, World world) { super(entityType, world); throw new UnsupportedOperationException(); @@ -29,25 +33,18 @@ protected GlowSquidEntityMixin(EntityType entityType, Wor @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(Builder builder, CallbackInfo ci) { - builder.add(RainglowEntity.GLOW_SQUID.getTrackedData(), RainglowColour.BLUE.getId()); + builder.add(THIS.getTrackedData(), THIS.getDefaultColour().getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, this.getDataTracker(), this.getRandom()); - nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); + nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour.getId()); } @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); - - // if read colour does not exist in the colour map, generate the squid a new one - if (Rainglow.colourUnloaded(RainglowEntity.GLOW_SQUID, colour)) { - colour = Rainglow.generateRandomColourId(this.getRandom()); - } - - this.setVariant(RainglowColour.get(colour)); + this.setVariant(THIS.readNbt(nbt, this.getRandom())); } /** @@ -56,22 +53,23 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { */ @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;addParticle(Lnet/minecraft/particle/ParticleEffect;DDDDDD)V"), cancellable = true) public void tickMovement(CallbackInfo ci) { - String colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, this.getDataTracker(), this.getRandom()); - if (!colour.equals(RainglowColour.BLUE.getId())) { + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); + + if (colour != RainglowColour.BLUE) { // we add 100 to g to let the mixin know that we want to override the method - this.getWorld().addParticle(ParticleTypes.GLOW, this.getParticleX(0.6), this.getRandomBodyY(), this.getParticleZ(0.6), Rainglow.getColourIndex(colour) + 100, 0, 0); + this.getWorld().addParticle(ParticleTypes.GLOW, this.getParticleX(0.6), this.getRandomBodyY(), this.getParticleZ(0.6), colour.ordinal() + 100, 0, 0); ci.cancel(); } } @Override public RainglowColour getVariant() { - return RainglowColour.get(Rainglow.getColour(RainglowEntity.GLOW_SQUID, this.getDataTracker(), this.getRandom())); + return Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); } @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(RainglowEntity.GLOW_SQUID.getTrackedData(), colour.getId()); + this.getDataTracker().set(THIS.getTrackedData(), colour.getId()); } @Mixin(SquidEntity.class) @@ -91,8 +89,8 @@ protected SquidEntityMixin(EntityType entityType, private int spawnParticles(ServerWorld instance, ParticleEffect particle, double x, double y, double z, int count, double deltaX, double deltaY, double deltaZ, double speed) { if (((Object) this) instanceof GlowSquidEntity) { // send in custom colour data - String colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, this.getDataTracker(), this.getRandom()); - int index = Rainglow.getColourIndex(colour); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); + int index = colour.ordinal(); // round x to 1 decimal place and append index data to the next two return ((ServerWorld) this.getWorld()).spawnParticles(particle, (Math.round(x * 10)) / 10D + index / 1000D, y + 0.5, z, 0, deltaX, deltaY, deltaZ, speed); } else { diff --git a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java index e026234..97f2415 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/SlimeEntityMixin.java @@ -14,6 +14,7 @@ import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -22,11 +23,11 @@ @Mixin(SlimeEntity.class) public abstract class SlimeEntityMixin extends Entity implements SlimeVariantProvider { - @Shadow - protected abstract ParticleEffect getParticles(); + @Unique + private static final RainglowEntity THIS = RainglowEntity.SLIME; @Shadow - public abstract int getSize(); + protected abstract ParticleEffect getParticles(); protected SlimeEntityMixin(EntityType entityType, World world) { super(entityType, world); @@ -35,24 +36,18 @@ protected SlimeEntityMixin(EntityType entityType, World w @Inject(method = "initDataTracker", at = @At("TAIL")) protected void initDataTracker(DataTracker.Builder builder, CallbackInfo ci) { - builder.add(RainglowEntity.SLIME.getTrackedData(), RainglowColour.LIME.getId()); + builder.add(THIS.getTrackedData(), THIS.getDefaultColour().getId()); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random); - nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.random); + nbt.putString(Rainglow.CUSTOM_NBT_KEY, colour.getId()); } @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { - String colour = nbt.getString(Rainglow.CUSTOM_NBT_KEY); - - if (Rainglow.colourUnloaded(RainglowEntity.SLIME, colour)) { - colour = Rainglow.generateRandomColourId(this.random); - } - - this.setVariant(RainglowColour.get(colour)); + this.setVariant(THIS.readNbt(nbt, this.random)); } /** @@ -60,8 +55,8 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { */ @Redirect(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) public boolean spawnWithParentColour(World instance, Entity entity) { - RainglowColour colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)); - entity.getDataTracker().set(RainglowEntity.SLIME.getTrackedData(), colour.getId()); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.random); + entity.getDataTracker().set(THIS.getTrackedData(), colour.getId()); return this.getWorld().spawnEntity(entity); } @@ -77,8 +72,8 @@ public boolean spawnWithParentColour(World instance, Entity entity) { ) public void tick(CallbackInfo ci) { float size = this.getDimensions(this.getPose()).width(); - String colour = RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)).getId(); - int index = Rainglow.getColourIndex(colour); + RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.random); + int index = colour.ordinal(); for (int j = 0; j < size / 2; j ++) { float f = this.random.nextFloat() * 6.2831855F; @@ -92,11 +87,11 @@ public void tick(CallbackInfo ci) { @Override public RainglowColour getVariant() { - return RainglowColour.get(Rainglow.getColour(RainglowEntity.SLIME, this.getDataTracker(), this.random)); + return Rainglow.getColour(THIS, this.getDataTracker(), this.random); } @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(RainglowEntity.SLIME.getTrackedData(), colour.getId()); + this.getDataTracker().set(THIS.getTrackedData(), colour.getId()); } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java index 28cb0a1..ec778f9 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java @@ -15,13 +15,13 @@ public class AllayEntityRendererMixin { @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(AllayEntity allayEntity, CallbackInfoReturnable cir) { - String colour = Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()); + RainglowColour colour = Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()); // if the colour is blue we don't need to override the method // this optimises a tiny bit - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.ALLAY) && !colour.equals(RainglowColour.BLUE.getId())) { - Identifier texture = Rainglow.getTexture(RainglowEntity.ALLAY, colour); - cir.setReturnValue(texture != null ? texture : Rainglow.getDefaultTexture(RainglowEntity.ALLAY)); + if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.ALLAY) && colour != RainglowEntity.ALLAY.getDefaultColour()) { + Identifier texture = Rainglow.getTexture(RainglowEntity.ALLAY, colour.getId()); + cir.setReturnValue(texture != null ? texture : RainglowEntity.ALLAY.getDefaultTexture()); } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowParticleMixin.java index df4dea5..60e163e 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowParticleMixin.java @@ -32,7 +32,7 @@ public void createParticle(DefaultParticleType defaultParticleType, ClientWorld GlowParticle glowParticle = new GlowParticle(clientWorld, d, e, f, 0.5 - GlowParticle.RANDOM.nextDouble(), h, 0.5 - GlowParticle.RANDOM.nextDouble(), this.spriteProvider); // we check the g value to see what the colour is - RainglowColour.RGB rgb = Rainglow.getPassiveParticleRGB((int) g, GlowParticle.RANDOM); + RainglowColour.RGB rgb = RainglowColour.getPassiveParticleRGB((int) g, GlowParticle.RANDOM); glowParticle.setColor(rgb.r(), rgb.g(), rgb.b()); // set velocities - I don't entirely understand why this is necessary, it's copied from vanilla code diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java index 53627a5..aba3c90 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java @@ -19,13 +19,13 @@ public class GlowSquidEntityRendererMixin { */ @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(GlowSquidEntity glowSquidEntity, CallbackInfoReturnable cir) { - String colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, glowSquidEntity.getDataTracker(), glowSquidEntity.getRandom()); + RainglowColour colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, glowSquidEntity.getDataTracker(), glowSquidEntity.getRandom()); // if the colour is blue we don't need to override the method // this optimises a tiny bit - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.GLOW_SQUID) && !colour.equals(RainglowColour.BLUE.getId())) { - Identifier texture = Rainglow.getTexture(RainglowEntity.GLOW_SQUID, colour); - cir.setReturnValue(texture != null ? texture : Rainglow.getDefaultTexture(RainglowEntity.GLOW_SQUID)); + if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.GLOW_SQUID) && colour != RainglowEntity.GLOW_SQUID.getDefaultColour()) { + Identifier texture = Rainglow.getTexture(RainglowEntity.GLOW_SQUID, colour.getId()); + cir.setReturnValue(texture != null ? texture : RainglowEntity.GLOW_SQUID.getDefaultTexture()); } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java index d93ede4..5800752 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java @@ -3,7 +3,6 @@ import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; -import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.render.entity.SlimeEntityRenderer; import net.minecraft.entity.mob.SlimeEntity; import net.minecraft.util.Identifier; @@ -16,12 +15,12 @@ public class SlimeEntityRendererMixin { @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(SlimeEntity entity, CallbackInfoReturnable cir) { - String colour = Rainglow.getColour(RainglowEntity.SLIME, entity.getDataTracker(), entity.getRandom()); + RainglowColour colour = Rainglow.getColour(RainglowEntity.SLIME, entity.getDataTracker(), entity.getRandom()); // don't override if the colour is lime, use the default texture - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME) && !colour.equals(RainglowColour.LIME.getId()) || Rainglow.CONFIG.getMode().equals(RainglowMode.get("vanilla"))) { - Identifier texture = Rainglow.getTexture(RainglowEntity.SLIME, colour); - cir.setReturnValue(texture != null ? texture : Rainglow.getDefaultTexture(RainglowEntity.SLIME)); + if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME) && colour != RainglowEntity.SLIME.getDefaultColour()) { + Identifier texture = Rainglow.getTexture(RainglowEntity.SLIME, colour.getId()); + cir.setReturnValue(texture != null ? texture : RainglowEntity.SLIME.getDefaultTexture()); } } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java index 81de85d..83327e6 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeParticleMixin.java @@ -25,7 +25,7 @@ public void createParticle(DefaultParticleType defaultParticleType, ClientWorld cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, new ItemStack(Items.SLIME_BALL))); // 99.9d and 100.1d are used to account for floating point errors } else if (h >= 99.9d && h <= 100.1d) { - ItemStack stack = Rainglow.getItem(RainglowEntity.SLIME, (int) g).getDefaultStack(); + ItemStack stack = RainglowEntity.SLIME.getItem((int) g).getDefaultStack(); cir.setReturnValue(new ItemBreakParticle(clientWorld, d, e, f, stack)); } else { cir.setReturnValue(null); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java index 797c136..a6ae80b 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SquidInkParticleMixin.java @@ -45,7 +45,7 @@ public void createParticle(DefaultParticleType defaultParticleType, ClientWorld // when we catch an exception we use white as it looks the most normal RainglowColour.RGB rgb; try { - rgb = Rainglow.getInkRgb(colourIndex); + rgb = RainglowColour.getInkRgb(colourIndex); } catch (IndexOutOfBoundsException ignored) { rgb = RainglowColour.WHITE.getInkRgb(); } From 3afe57fb93d30d7a47c84edff57160e082b57d86 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 May 2024 19:45:20 -0500 Subject: [PATCH 27/40] deduplicate a bit --- .../ix0rai/rainglow/data/RainglowColour.java | 35 ++++++++++++------- .../ix0rai/rainglow/data/RainglowEntity.java | 13 +++++++ .../client/AllayEntityRendererMixin.java | 11 +----- .../client/GlowSquidEntityRendererMixin.java | 11 +----- .../client/SlimeEntityRendererMixin.java | 10 +----- 5 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java index 25a88c5..f87b2e9 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowColour.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.Map; public enum RainglowColour { BLACK("black", new RGB(0.0F, 0.0F, 0.0F), new RGB(0.0F, 0.0F, 0.0F), new RGB(0, 0, 0), Items.BLACK_DYE), @@ -35,7 +36,7 @@ public enum RainglowColour { } private final String id; - private Identifier texture; + private final Map textures; private final RGB passiveParticleRgb; private final RGB altPassiveParticleRgb; private final RGB inkRgb; @@ -43,7 +44,7 @@ public enum RainglowColour { RainglowColour(String id, RGB passiveParticleRgb, RGB altPassiveParticleRgb, RGB inkRgb, Item item) { this.id = id; - this.texture = new Identifier("textures/entity/squid/" + this.getId() + ".png"); + this.textures = new HashMap<>(); this.passiveParticleRgb = passiveParticleRgb; this.altPassiveParticleRgb = altPassiveParticleRgb; this.inkRgb = inkRgb; @@ -51,19 +52,27 @@ public enum RainglowColour { } public Identifier getTexture(RainglowEntity entityType) { - // use minecraft's textures when possible, so we can ship fewer textures - if (entityType == RainglowEntity.GLOW_SQUID) { - String textureName = this.getId().equals("blue") ? "glow_squid" : this.getId(); - this.texture = new Identifier("textures/entity/squid/" + textureName + ".png"); - } else if (entityType == RainglowEntity.ALLAY) { - String textureName = this.getId().equals("blue") ? "allay" : this.getId(); - this.texture = new Identifier("textures/entity/allay/" + textureName + ".png"); - } else { - String textureName = this.getId().equals("lime") ? "slime" : this.getId(); - this.texture = new Identifier("textures/entity/slime/" + textureName + ".png"); + if (this.textures.isEmpty()) { + for (RainglowEntity entity : RainglowEntity.values()) { + // use minecraft's textures when possible, so we can ship fewer textures + switch (entity) { + case GLOW_SQUID -> { + String textureName = RainglowEntity.GLOW_SQUID.getDefaultColour() == this ? "glow_squid" : this.getId(); + this.textures.put(entity, new Identifier("textures/entity/squid/" + textureName + ".png")); + } + case ALLAY -> { + String textureName = RainglowEntity.ALLAY.getDefaultColour() == this ? "allay" : this.getId(); + this.textures.put(entity, new Identifier("textures/entity/allay/" + textureName + ".png")); + } + case SLIME -> { + String textureName = RainglowEntity.SLIME.getDefaultColour() == this ? "slime" : this.getId(); + this.textures.put(entity, new Identifier("textures/entity/slime/" + textureName + ".png")); + } + } + } } - return this.texture; + return this.textures.get(entityType); } public String getId() { diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index a23eb22..0e5118a 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -1,6 +1,7 @@ package io.ix0rai.rainglow.data; import io.ix0rai.rainglow.Rainglow; +import net.minecraft.entity.Entity; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -13,6 +14,7 @@ import net.minecraft.util.Identifier; import net.minecraft.util.random.RandomGenerator; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Arrays; import java.util.HashMap; @@ -83,4 +85,15 @@ public static void write(PacketByteBuf buf, RainglowEntity entity) { public static RainglowEntity get(String id) { return BY_ID.get(id); } + + public void overrideTexture(Entity entity, CallbackInfoReturnable cir) { + RainglowColour colour = Rainglow.getColour(this, entity.getDataTracker(), entity.getWorld().getRandom()); + + // if the colour is default we don't need to override the method + // this optimises a tiny bit + if (Rainglow.CONFIG.isEntityEnabled(this) && colour != this.getDefaultColour()) { + Identifier texture = Rainglow.getTexture(this, colour.getId()); + cir.setReturnValue(texture != null ? texture : this.getDefaultTexture()); + } + } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java index ec778f9..7df11b2 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/AllayEntityRendererMixin.java @@ -1,7 +1,5 @@ package io.ix0rai.rainglow.mixin.client; -import io.ix0rai.rainglow.Rainglow; -import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import net.minecraft.client.render.entity.AllayEntityRenderer; import net.minecraft.entity.passive.AllayEntity; @@ -15,13 +13,6 @@ public class AllayEntityRendererMixin { @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(AllayEntity allayEntity, CallbackInfoReturnable cir) { - RainglowColour colour = Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()); - - // if the colour is blue we don't need to override the method - // this optimises a tiny bit - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.ALLAY) && colour != RainglowEntity.ALLAY.getDefaultColour()) { - Identifier texture = Rainglow.getTexture(RainglowEntity.ALLAY, colour.getId()); - cir.setReturnValue(texture != null ? texture : RainglowEntity.ALLAY.getDefaultTexture()); - } + RainglowEntity.ALLAY.overrideTexture(allayEntity, cir); } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java index aba3c90..183d38a 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/GlowSquidEntityRendererMixin.java @@ -1,7 +1,5 @@ package io.ix0rai.rainglow.mixin.client; -import io.ix0rai.rainglow.Rainglow; -import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import net.minecraft.client.render.entity.GlowSquidEntityRenderer; import net.minecraft.entity.passive.GlowSquidEntity; @@ -19,13 +17,6 @@ public class GlowSquidEntityRendererMixin { */ @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(GlowSquidEntity glowSquidEntity, CallbackInfoReturnable cir) { - RainglowColour colour = Rainglow.getColour(RainglowEntity.GLOW_SQUID, glowSquidEntity.getDataTracker(), glowSquidEntity.getRandom()); - - // if the colour is blue we don't need to override the method - // this optimises a tiny bit - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.GLOW_SQUID) && colour != RainglowEntity.GLOW_SQUID.getDefaultColour()) { - Identifier texture = Rainglow.getTexture(RainglowEntity.GLOW_SQUID, colour.getId()); - cir.setReturnValue(texture != null ? texture : RainglowEntity.GLOW_SQUID.getDefaultTexture()); - } + RainglowEntity.GLOW_SQUID.overrideTexture(glowSquidEntity, cir); } } diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java index 5800752..fe16520 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/SlimeEntityRendererMixin.java @@ -1,7 +1,5 @@ package io.ix0rai.rainglow.mixin.client; -import io.ix0rai.rainglow.Rainglow; -import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import net.minecraft.client.render.entity.SlimeEntityRenderer; import net.minecraft.entity.mob.SlimeEntity; @@ -15,12 +13,6 @@ public class SlimeEntityRendererMixin { @Inject(method = "getTexture*", at = @At("HEAD"), cancellable = true) public void getTexture(SlimeEntity entity, CallbackInfoReturnable cir) { - RainglowColour colour = Rainglow.getColour(RainglowEntity.SLIME, entity.getDataTracker(), entity.getRandom()); - - // don't override if the colour is lime, use the default texture - if (Rainglow.CONFIG.isEntityEnabled(RainglowEntity.SLIME) && colour != RainglowEntity.SLIME.getDefaultColour()) { - Identifier texture = Rainglow.getTexture(RainglowEntity.SLIME, colour.getId()); - cir.setReturnValue(texture != null ? texture : RainglowEntity.SLIME.getDefaultTexture()); - } + RainglowEntity.SLIME.overrideTexture(entity, cir); } } From d8986bded2b686f11388aa768a571aa8a078d437 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 May 2024 19:48:57 -0500 Subject: [PATCH 28/40] bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2524614..a0f3969 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ quilt_mappings=6 loader_version=0.15.11 # mod properties -mod_version=1.2.0+mc1.20.6 +mod_version=1.3.0+mc1.20.6 maven_group=rainglow archives_base_name=rainglow From c13409c43e36884ca2bc4f6f8cd481d7fc26adaf Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 May 2024 20:02:18 -0500 Subject: [PATCH 29/40] remove some duplication --- src/main/java/io/ix0rai/rainglow/Rainglow.java | 18 ------------------ .../ix0rai/rainglow/data/RainglowEntity.java | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index d701768..98094ce 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -29,9 +29,6 @@ public class Rainglow implements ModInitializer { public static final Gson GSON = new Gson(); private static final List COLOURS = new ArrayList<>(); - private static final Map GLOW_SQUID_TEXTURES = new HashMap<>(); - private static final Map ALLAY_TEXTURES = new HashMap<>(); - private static final Map SLIME_TEXTURES = new HashMap<>(); public static final String CUSTOM_NBT_KEY = "Colour"; @@ -61,9 +58,6 @@ public static void setMode(RainglowMode mode) { LOGGER.warn("attempted to load missing mode, resetting to rainbow"); } - GLOW_SQUID_TEXTURES.clear(); - ALLAY_TEXTURES.clear(); - SLIME_TEXTURES.clear(); COLOURS.clear(); List colours = mode.getColours(); @@ -79,23 +73,11 @@ public static void setMode(RainglowMode mode) { private static void addColour(RainglowColour colour) { COLOURS.add(colour); - GLOW_SQUID_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.GLOW_SQUID)); - ALLAY_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.ALLAY)); - SLIME_TEXTURES.put(colour.getId(), colour.getTexture(RainglowEntity.SLIME)); - if (COLOURS.size() >= 100) { throw new RuntimeException("Too many colours registered! Only up to 99 are allowed"); } } - public static Identifier getTexture(RainglowEntity entityType, String colour) { - return switch (entityType) { - case ALLAY -> ALLAY_TEXTURES.get(colour); - case SLIME -> SLIME_TEXTURES.get(colour); - case GLOW_SQUID -> GLOW_SQUID_TEXTURES.get(colour); - }; - } - public static String generateRandomColourId(RandomGenerator random) { return COLOURS.get(random.nextInt(COLOURS.size())).getId(); } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index 0e5118a..f0d041b 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -92,7 +92,7 @@ public void overrideTexture(Entity entity, CallbackInfoReturnable ci // if the colour is default we don't need to override the method // this optimises a tiny bit if (Rainglow.CONFIG.isEntityEnabled(this) && colour != this.getDefaultColour()) { - Identifier texture = Rainglow.getTexture(this, colour.getId()); + Identifier texture = colour.getTexture(this); cir.setReturnValue(texture != null ? texture : this.getDefaultTexture()); } } From dd63b3cbedb854816c46a9b51001f6805181b454 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 May 2024 21:01:03 -0500 Subject: [PATCH 30/40] remove tons of useless garbage --- .../java/io/ix0rai/rainglow/Rainglow.java | 36 ++--------------- .../rainglow/client/RainglowClient.java | 16 +------- .../rainglow/config/CustomModeScreen.java | 6 --- .../rainglow/config/RainglowConfig.java | 40 +++++-------------- .../rainglow/config/RainglowConfigScreen.java | 4 +- .../io/ix0rai/rainglow/data/RainglowMode.java | 24 +++-------- .../data/RainglowResourceReloader.java | 8 ---- 7 files changed, 23 insertions(+), 111 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index 98094ce..e5b5e53 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -18,8 +18,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; - public class Rainglow implements ModInitializer { public static final String MOD_ID = "rainglow"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); @@ -28,8 +26,6 @@ public class Rainglow implements ModInitializer { public static final RainglowConfig CONFIG = RainglowConfig.create(ENVIRONMENT, "", MOD_ID, RainglowConfig.class); public static final Gson GSON = new Gson(); - private static final List COLOURS = new ArrayList<>(); - public static final String CUSTOM_NBT_KEY = "Colour"; @Override @@ -52,38 +48,14 @@ public static Identifier id(String id) { return new Identifier(MOD_ID, id); } - public static void setMode(RainglowMode mode) { - if (mode == null) { - mode = RainglowMode.get("rainbow"); - LOGGER.warn("attempted to load missing mode, resetting to rainbow"); - } - - COLOURS.clear(); - - List colours = mode.getColours(); - if (colours.isEmpty()) { - LOGGER.info("No colours were present in the internal collection, adding blue so that the game doesn't crash"); - colours.add(RainglowColour.BLUE); - } - - colours.forEach(Rainglow::addColour); - CONFIG.setInitialized(); - } - - private static void addColour(RainglowColour colour) { - COLOURS.add(colour); - - if (COLOURS.size() >= 100) { - throw new RuntimeException("Too many colours registered! Only up to 99 are allowed"); - } - } - public static String generateRandomColourId(RandomGenerator random) { - return COLOURS.get(random.nextInt(COLOURS.size())).getId(); + var colours = CONFIG.getMode().getColours(); + return colours.get(random.nextInt(colours.size())).getId(); } public static boolean colourUnloaded(RainglowEntity entityType, String colour) { - return !COLOURS.contains(RainglowColour.get(colour)) && !colour.equals(entityType.getDefaultColour().getId()); + var colours = CONFIG.getMode().getColours(); + return !colours.contains(RainglowColour.get(colour)) && !colour.equals(entityType.getDefaultColour().getId()); } public static String translatableTextKey(String key) { diff --git a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java index 1907cd2..c84c628 100644 --- a/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java +++ b/src/main/java/io/ix0rai/rainglow/client/RainglowClient.java @@ -47,9 +47,6 @@ public void onInitializeClient() { } Rainglow.CONFIG.toggles.setOverride(toggles.build()); - // lock the config from reloading on resource reload - Rainglow.CONFIG.setEditLocked(true); - // log Rainglow.LOGGER.info("received config from server: set mode to " + payload.currentMode() + " and custom colours to " + payload.customMode()); }); @@ -69,10 +66,6 @@ public void onInitializeClient() { } } - // now that we have modes, we can load the config - if (!Rainglow.CONFIG.isInitialized()) { - Rainglow.setMode(Rainglow.CONFIG.getMode()); - } // log if (!newModeIds.isEmpty()) { @@ -83,13 +76,8 @@ public void onInitializeClient() { ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> client.execute(() -> { - if (Rainglow.CONFIG.isEditLocked(client)) { - // unlock config - Rainglow.CONFIG.setEditLocked(false); - - // reset values to those configured in file - Rainglow.CONFIG.values().forEach(TrackedValue::removeOverride); - } + // reset values to those configured in file + Rainglow.CONFIG.values().forEach(TrackedValue::removeOverride); }) ); diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index ca8600d..aa74a05 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -2,7 +2,6 @@ import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; -import io.ix0rai.rainglow.data.RainglowMode; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.GameOptionsScreen; @@ -73,11 +72,6 @@ private void save() { } Rainglow.CONFIG.save(); - - // refresh colours of custom mode - if (Rainglow.CONFIG.getMode().getId().equals("custom")) { - Rainglow.setMode(RainglowMode.get("custom")); - } } @Override diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java index 08948eb..e5c45ae 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfig.java @@ -7,10 +7,10 @@ import folk.sisby.kaleido.lib.quiltconfig.api.values.TrackedValue; import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueList; import folk.sisby.kaleido.lib.quiltconfig.api.values.ValueMap; +import io.ix0rai.rainglow.Rainglow; import io.ix0rai.rainglow.data.RainglowColour; import io.ix0rai.rainglow.data.RainglowEntity; import io.ix0rai.rainglow.data.RainglowMode; -import net.minecraft.client.MinecraftClient; import java.util.HashMap; import java.util.List; @@ -29,11 +29,16 @@ public class RainglowConfig extends ReflectiveConfig { @Comment("The custom colours to use when the mode is set to custom.") public final TrackedValue> customColours = this.list("", RainglowMode.getDefaultCustom().stream().map(RainglowColour::getId).toArray(String[]::new)); - private transient boolean editLocked = false; - private transient boolean initialized = false; - public RainglowMode getMode() { - return RainglowMode.get(this.mode.value()); + var mode = RainglowMode.get(this.mode.value()); + + if (mode == null) { + Rainglow.LOGGER.warn("unknown mode {}, defaulting to rainbow", this.mode.value()); + this.mode.setValue("rainbow"); + return getMode(); + } + + return mode; } public List getCustom() { @@ -62,35 +67,10 @@ public boolean isEntityEnabled(RainglowEntity entity) { return this.toggles.value().get(entity.getId()); } - public void setEntityEnabled(RainglowEntity entity, boolean enabled) { - this.toggles.value().put(entity.getId(), enabled); - } - public int getRarity(RainglowEntity entity) { return this.rarities.value().get(entity.getId()); } - public void setRarity(RainglowEntity entity, int rarity) { - this.rarities.value().put(entity.getId(), rarity); - } - - public boolean isEditLocked(MinecraftClient client) { - // client can only be locked inside a multiplayer server - return !client.isInSingleplayer() && (client.getCurrentServerEntry() != null && this.editLocked); - } - - public void setEditLocked(boolean editLocked) { - this.editLocked = editLocked; - } - - public boolean isInitialized() { - return this.initialized; - } - - public void setInitialized() { - this.initialized = true; - } - /** * creates a map of default values for each {@link RainglowEntity} */ diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 5c01b5b..7991b9b 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -156,7 +156,6 @@ private void save() { } Rainglow.CONFIG.mode.setValue(this.mode.getId()); - Rainglow.setMode(RainglowMode.get(this.mode.getId())); } private Tooltip createColourListLabel(RainglowMode mode) { @@ -199,7 +198,8 @@ public void closeScreen(boolean saved) { this.isConfirming = true; this.clearAndInit(); } else { - if (Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance())) { + // overrides will exist when connected to a server syncing its values + if (Rainglow.CONFIG.mode.isBeingOverridden()) { sendConfigLockedToast(); } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java index 97a23e2..4c94a53 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowMode.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -33,7 +32,7 @@ public RainglowMode(JsonMode mode, boolean existsLocally) { public RainglowMode(String id, List colourIds, Text text, boolean existsLocally) { if (!id.matches("^[a-z0-9_]+$")) { - throw new IllegalArgumentException("loaded rainglow mode with id " + id + " which contains invalid characters"); + Rainglow.LOGGER.error("loaded rainglow mode with id {} which contains invalid characters! (only lowercase letters, numbers, and underscores are allowed)", id); } this.id = id; @@ -47,6 +46,10 @@ public RainglowMode(String id, List colourIds, Text text, boolean exists this.colours.add(RainglowColour.get(colour)); } + if (this.colours.isEmpty() && !id.equals("all_colours") && !id.equals("custom")) { + Rainglow.LOGGER.error("cannot load mode with id {}: no colours found!", id); + } + this.text = text; this.existsLocally = existsLocally; @@ -63,23 +66,6 @@ public List getColours() { }; } - public RainglowMode cycle() { - // cycle to next in list, wrapping around to 0 if the next ordinal is larger than the map's size - Collection values = MODES.values(); - Iterator iterator = values.iterator(); - - // look for matching key and return next mode - while (iterator.hasNext()) { - RainglowMode mode = iterator.next(); - if (mode.id.equals(this.id) && iterator.hasNext()) { - return iterator.next(); - } - } - - // otherwise return first mode - return values.iterator().next(); - } - @Override public String toString() { return this.getId(); diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java index 5c91c4c..fd4ee44 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java @@ -1,13 +1,10 @@ package io.ix0rai.rainglow.data; import io.ix0rai.rainglow.Rainglow; -import net.fabricmc.api.EnvType; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; -import net.minecraft.client.MinecraftClient; import java.io.IOException; import java.io.InputStream; @@ -45,10 +42,5 @@ default void reload(ResourceManager manager) { } this.log(); - - // load config - if (!Rainglow.CONFIG.isInitialized() || (FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT) && !Rainglow.CONFIG.isEditLocked(MinecraftClient.getInstance()))) { - Rainglow.setMode(Rainglow.CONFIG.getMode()); - } } } From 89c43b8d76dc3434b01185d51033affb0ad2b9d6 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 May 2024 21:47:17 -0500 Subject: [PATCH 31/40] generify more mixins, fix colouring via item --- .../ix0rai/rainglow/data/RainglowEntity.java | 31 +++++++++++-- .../rainglow/mixin/AllayEntityMixin.java | 6 +-- .../ix0rai/rainglow/mixin/DyeItemMixin.java | 35 +++------------ .../ix0rai/rainglow/mixin/MobEntityMixin.java | 44 +++---------------- 4 files changed, 43 insertions(+), 73 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java index f0d041b..b8eba45 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java @@ -2,6 +2,7 @@ import io.ix0rai.rainglow.Rainglow; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityData; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -14,15 +15,17 @@ import net.minecraft.util.Identifier; import net.minecraft.util.random.RandomGenerator; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Arrays; import java.util.HashMap; +import java.util.function.Function; public enum RainglowEntity { - GLOW_SQUID("glow_squid", RainglowColour.BLUE, DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING)), - ALLAY("allay", RainglowColour.BLUE, DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING)), - SLIME("slime", RainglowColour.LIME, DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING)); + GLOW_SQUID("glow_squid", RainglowColour.BLUE, DataTracker.registerData(GlowSquidEntity.class, TrackedDataHandlerRegistry.STRING), GlowSquidEntityData::new), + ALLAY("allay", RainglowColour.BLUE, DataTracker.registerData(AllayEntity.class, TrackedDataHandlerRegistry.STRING), AllayEntityData::new), + SLIME("slime", RainglowColour.LIME, DataTracker.registerData(SlimeEntity.class, TrackedDataHandlerRegistry.STRING), SlimeEntityData::new); private static final HashMap BY_ID = new HashMap<>(); static { @@ -32,11 +35,13 @@ public enum RainglowEntity { private final String id; private final RainglowColour defaultColour; private final TrackedData trackedData; + private final Function entityDataFactory; - RainglowEntity(String id, RainglowColour defaultColour, TrackedData trackedData) { + RainglowEntity(String id, RainglowColour defaultColour, TrackedData trackedData, Function entityDataFactory) { this.id = id; this.defaultColour = defaultColour; this.trackedData = trackedData; + this.entityDataFactory = entityDataFactory; } public String getId() { @@ -55,6 +60,10 @@ public Identifier getDefaultTexture() { return this.defaultColour.getTexture(this); } + public EntityData createEntityData(RainglowColour colour) { + return this.entityDataFactory.apply(colour); + } + public Item getItem(int index) { if (index == -1) { return this.getDefaultColour().getItem(); @@ -86,6 +95,20 @@ public static RainglowEntity get(String id) { return BY_ID.get(id); } + @Unique + @SuppressWarnings("all") + public static RainglowEntity get(Entity entity) { + if (entity instanceof GlowSquidEntity) { + return GLOW_SQUID; + } else if (entity instanceof AllayEntity) { + return ALLAY; + } else if (entity instanceof SlimeEntity) { + return SLIME; + } + + return null; + } + public void overrideTexture(Entity entity, CallbackInfoReturnable cir) { RainglowColour colour = Rainglow.getColour(this, entity.getDataTracker(), entity.getWorld().getRandom()); diff --git a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java index 1a6a57d..0890197 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/AllayEntityMixin.java @@ -48,18 +48,18 @@ public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { @Redirect(method = "duplicate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) public boolean spawnWithColour(World instance, Entity entity) { RainglowColour colour = Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); - entity.getDataTracker().set(RainglowEntity.ALLAY.getTrackedData(), colour.getId()); + entity.getDataTracker().set(THIS.getTrackedData(), colour.getId()); return this.getWorld().spawnEntity(entity); } @Override public RainglowColour getVariant() { - return Rainglow.getColour(RainglowEntity.ALLAY, this.getDataTracker(), this.getRandom()); + return Rainglow.getColour(THIS, this.getDataTracker(), this.getRandom()); } @Override public void setVariant(RainglowColour colour) { - this.getDataTracker().set(RainglowEntity.ALLAY.getTrackedData(), colour.getId()); + this.getDataTracker().set(THIS.getTrackedData(), colour.getId()); } @Unique diff --git a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java index f462935..36be191 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java @@ -4,9 +4,6 @@ import io.ix0rai.rainglow.data.RainglowEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.mob.SlimeEntity; -import net.minecraft.entity.passive.AllayEntity; -import net.minecraft.entity.passive.GlowSquidEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.DyeItem; import net.minecraft.item.ItemStack; @@ -27,40 +24,20 @@ public class DyeItemMixin { @Inject(method = "useOnEntity", at = @At("TAIL"), cancellable = true) private void useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand, CallbackInfoReturnable cir) { String colour = getDye(stack); + RainglowEntity entityType = RainglowEntity.get(entity); - if (entity instanceof GlowSquidEntity glowSquid && glowSquid.isAlive() && !Rainglow.getColour(RainglowEntity.GLOW_SQUID, glowSquid.getDataTracker(), glowSquid.getRandom()).equals(colour)) { - glowSquid.getWorld().playSoundFromEntity(user, glowSquid, SoundEvents.BLOCK_AMETHYST_BLOCK_CHIME, SoundCategory.PLAYERS, 1.0f, 1.0f); + if (entityType != null && !Rainglow.colourUnloaded(entityType, colour) + && !Rainglow.getColour(entityType, entity.getDataTracker(), entity.getWorld().getRandom()).getId().equals(colour)) { + entity.getWorld().playSoundFromEntity(user, entity, SoundEvents.BLOCK_AMETHYST_CLUSTER_BREAK, SoundCategory.PLAYERS, 5.0f, 1.0f); if (!user.getWorld().isClient()) { stack.decrement(1); } - DataTracker tracker = glowSquid.getDataTracker(); - tracker.set(RainglowEntity.GLOW_SQUID.getTrackedData(), colour); - - cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); - } else if (entity instanceof AllayEntity allayEntity && allayEntity.isAlive() && !Rainglow.getColour(RainglowEntity.ALLAY, allayEntity.getDataTracker(), allayEntity.getRandom()).equals(colour)) { - allayEntity.getWorld().playSoundFromEntity(user, allayEntity, SoundEvents.BLOCK_AMETHYST_BLOCK_CHIME, SoundCategory.PLAYERS, 1.0f, 1.0f); - if (!user.getWorld().isClient()) { - stack.decrement(1); - } - - DataTracker tracker = allayEntity.getDataTracker(); - tracker.set(RainglowEntity.ALLAY.getTrackedData(), colour); - - cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); - } else if (entity instanceof SlimeEntity slimeEntity && slimeEntity.isAlive() && !Rainglow.getColour(RainglowEntity.SLIME, slimeEntity.getDataTracker(), slimeEntity.getRandom()).equals(colour)) { - slimeEntity.getWorld().playSoundFromEntity(user, slimeEntity, SoundEvents.BLOCK_AMETHYST_BLOCK_CHIME, SoundCategory.PLAYERS, 1.0f, 1.0f); - if (!user.getWorld().isClient()) { - stack.decrement(1); - } - - DataTracker tracker = slimeEntity.getDataTracker(); - tracker.set(RainglowEntity.SLIME.getTrackedData(), colour); + DataTracker tracker = entity.getDataTracker(); + tracker.set(entityType.getTrackedData(), colour); cir.setReturnValue(ActionResult.success(user.getWorld().isClient())); } - - cir.setReturnValue(ActionResult.PASS); } @Unique diff --git a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java index d41e488..495bf2d 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/MobEntityMixin.java @@ -1,22 +1,14 @@ package io.ix0rai.rainglow.mixin; import io.ix0rai.rainglow.Rainglow; -import io.ix0rai.rainglow.data.AllayEntityData; -import io.ix0rai.rainglow.data.AllayVariantProvider; import io.ix0rai.rainglow.data.RainglowColour; -import io.ix0rai.rainglow.data.GlowSquidEntityData; -import io.ix0rai.rainglow.data.GlowSquidVariantProvider; import io.ix0rai.rainglow.data.RainglowEntity; -import io.ix0rai.rainglow.data.SlimeEntityData; -import io.ix0rai.rainglow.data.SlimeVariantProvider; import net.minecraft.entity.EntityData; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.SpawnReason; +import net.minecraft.entity.VariantProvider; import net.minecraft.entity.mob.MobEntity; -import net.minecraft.entity.mob.SlimeEntity; -import net.minecraft.entity.passive.AllayEntity; -import net.minecraft.entity.passive.GlowSquidEntity; import net.minecraft.world.LocalDifficulty; import net.minecraft.world.ServerWorldAccess; import net.minecraft.world.World; @@ -36,41 +28,19 @@ protected MobEntityMixin(EntityType entityType, World world @SuppressWarnings("all") @Inject(method = "initialize", at = @At("RETURN"), cancellable = true) public void initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @Nullable EntityData entityData, CallbackInfoReturnable cir) { - if ((Object) this instanceof GlowSquidEntity glowSquid) { - RainglowColour colour = generateColour(); - ((GlowSquidVariantProvider) glowSquid).setVariant(colour); - cir.setReturnValue(new GlowSquidEntityData(colour)); - } else if ((Object) this instanceof AllayEntity allay) { - RainglowColour colour = generateColour(); - ((AllayVariantProvider) allay).setVariant(colour); - cir.setReturnValue(new AllayEntityData(colour)); - } else if ((Object) this instanceof SlimeEntity slime) { - RainglowColour colour = generateColour(); - ((SlimeVariantProvider) slime).setVariant(colour); - cir.setReturnValue(new SlimeEntityData(colour)); + RainglowEntity entity = RainglowEntity.get(this); + if (entity != null) { + RainglowColour colour = generateColour(entity); + ((VariantProvider) this).setVariant(colour); + cir.setReturnValue(entity.createEntityData(colour)); } } @Unique - private RainglowColour generateColour() { - RainglowEntity entity = getCurrentEntity(); + private RainglowColour generateColour(RainglowEntity entity) { int i = random.nextInt(100); int rarity = Rainglow.CONFIG.getRarity(entity); return i >= rarity ? entity.getDefaultColour() : RainglowColour.get(Rainglow.generateRandomColourId(this.random)); } - - @Unique - @SuppressWarnings("all") - private RainglowEntity getCurrentEntity() { - if ((Object) this instanceof GlowSquidEntity) { - return RainglowEntity.GLOW_SQUID; - } else if ((Object) this instanceof AllayEntity) { - return RainglowEntity.ALLAY; - } else if ((Object) this instanceof SlimeEntity) { - return RainglowEntity.SLIME; - } else { - throw new RuntimeException("unsupported entity"); - } - } } From 8cfdae59806b4020c23b0b9b856cc40c2ab5a87c Mon Sep 17 00:00:00 2001 From: ix0rai Date: Fri, 24 May 2024 16:44:05 -0500 Subject: [PATCH 32/40] coloured yes/no text --- .../java/io/ix0rai/rainglow/config/DeferredSaveOption.java | 3 +-- .../java/io/ix0rai/rainglow/config/RainglowConfigScreen.java | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java index 201b7eb..ff1b927 100644 --- a/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java +++ b/src/main/java/io/ix0rai/rainglow/config/DeferredSaveOption.java @@ -4,7 +4,6 @@ import io.ix0rai.rainglow.Rainglow; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.Option; -import net.minecraft.text.CommonTexts; import java.util.function.Consumer; @@ -44,7 +43,7 @@ public static DeferredSaveOption createDeferredBoolean(String key, Stri return new DeferredSaveOption<>( Rainglow.translatableTextKey(key), tooltip != null ? Option.constantTooltip(Rainglow.translatableText(tooltip)) : Option.emptyTooltip(), - (text, value) -> value ? CommonTexts.YES : CommonTexts.NO, + (text, value) -> value ? RainglowConfigScreen.YES : RainglowConfigScreen.NO, BOOLEAN_VALUES, defaultValue, updateCallback, diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 7991b9b..6293435 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -30,6 +30,8 @@ public class RainglowConfigScreen extends Screen { private static final Text TITLE = Rainglow.translatableText("config.title"); + public static final Text YES = Text.translatable("gui.yes").styled(style -> style.withColor(0x00FF00)); + public static final Text NO = Text.translatable("gui.no").styled(style -> style.withColor(0xFF0000)); private final Screen parent; private final Map> toggles = new HashMap<>(); From cb06ed356ebecefb6c3c573c1b3854fca774175f Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 16:54:48 -0500 Subject: [PATCH 33/40] update some requirements --- src/main/resources/fabric.mod.json | 2 +- src/main/resources/rainglow.mixins.json | 2 +- src/main/resources/rainglow.toml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 src/main/resources/rainglow.toml diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index a33a321..e7e8104 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -39,7 +39,7 @@ "fabricloader": ">=0.14.19", "fabric-resource-loader-v0": "*", "fabric-networking-api-v1": "*", - "minecraft": ">=1.20.2" + "minecraft": ">=1.20.6" }, "suggests": { diff --git a/src/main/resources/rainglow.mixins.json b/src/main/resources/rainglow.mixins.json index 9836a06..7571fc2 100644 --- a/src/main/resources/rainglow.mixins.json +++ b/src/main/resources/rainglow.mixins.json @@ -2,7 +2,7 @@ "required": true, "minVersion": "0.8", "package": "io.ix0rai.rainglow.mixin", - "compatibilityLevel": "JAVA_17", + "compatibilityLevel": "JAVA_21", "mixins": [ "AllayEntityMixin", "DyeItemMixin", diff --git a/src/main/resources/rainglow.toml b/src/main/resources/rainglow.toml deleted file mode 100644 index bf90fda..0000000 --- a/src/main/resources/rainglow.toml +++ /dev/null @@ -1 +0,0 @@ -mode = "rainbow" \ No newline at end of file From c62758625a078c182b507ca382d83221d5f18bb1 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 16:57:04 -0500 Subject: [PATCH 34/40] curse you misleading fabric docs --- .../rainglow/mixin/client/screen/CyclingValueSetMixin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java index 57ee0ad..5acb14f 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/client/screen/CyclingValueSetMixin.java @@ -11,7 +11,6 @@ public interface CyclingValueSetMixin { @ModifyArg( method = "method_42723", - remap = false, at = @At( value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/button/CyclingButtonWidget$Builder;initially(Ljava/lang/Object;)Lnet/minecraft/client/gui/widget/button/CyclingButtonWidget$Builder;" From 33a06e6501fc7adfdc6c6653743f370bf92c9499 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 17:01:11 -0500 Subject: [PATCH 35/40] new save button behaviour @orifu --- .../io/ix0rai/rainglow/config/CustomModeScreen.java | 2 +- .../ix0rai/rainglow/config/RainglowConfigScreen.java | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index aa74a05..95a7a25 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -40,7 +40,6 @@ public CustomModeScreen(Screen parent) { sendNoColoursToast(); } else { this.save(); - this.closeScreen(); } }).build(); this.saveButton.active = false; @@ -72,6 +71,7 @@ private void save() { } Rainglow.CONFIG.save(); + this.saveButton.active = false; } @Override diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 6293435..902da74 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -45,10 +45,7 @@ public RainglowConfigScreen(@Nullable Screen parent) { super(TITLE); this.parent = parent; this.mode = RainglowMode.get(Rainglow.CONFIG.mode.getRealValue()); - this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> { - this.save(); - this.closeScreen(true); - }).build(); + this.saveButton = ButtonWidget.builder(Rainglow.translatableText("config.save"), button -> this.save()).build(); this.saveButton.active = false; } @@ -158,6 +155,7 @@ private void save() { } Rainglow.CONFIG.mode.setValue(this.mode.getId()); + this.saveButton.active = false; } private Tooltip createColourListLabel(RainglowMode mode) { @@ -192,11 +190,7 @@ private Tooltip createColourListLabel(RainglowMode mode) { @Override public void closeScreen() { - this.closeScreen(false); - } - - public void closeScreen(boolean saved) { - if (!saved && this.saveButton.active) { + if (this.saveButton.active) { this.isConfirming = true; this.clearAndInit(); } else { From cf46e97919654d37e20f84898edab9aad05e232c Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 17:09:00 -0500 Subject: [PATCH 36/40] disable dying mobs when they aren't enabled @orifu --- src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java index 36be191..95e8bc3 100644 --- a/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java +++ b/src/main/java/io/ix0rai/rainglow/mixin/DyeItemMixin.java @@ -27,6 +27,7 @@ private void useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity RainglowEntity entityType = RainglowEntity.get(entity); if (entityType != null && !Rainglow.colourUnloaded(entityType, colour) + && Rainglow.CONFIG.isEntityEnabled(entityType) && !Rainglow.getColour(entityType, entity.getDataTracker(), entity.getWorld().getRandom()).getId().equals(colour)) { entity.getWorld().playSoundFromEntity(user, entity, SoundEvents.BLOCK_AMETHYST_CLUSTER_BREAK, SoundCategory.PLAYERS, 5.0f, 1.0f); if (!user.getWorld().isClient()) { From 00f77ca59e7069a20f253ecaf67a6085a2ddd8a0 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 17:53:53 -0500 Subject: [PATCH 37/40] add more info on the config screen @orifu --- .../java/io/ix0rai/rainglow/Rainglow.java | 7 ++++- .../rainglow/config/RainglowConfigScreen.java | 26 ++++++++----------- .../data/RainglowResourceReloader.java | 6 +++++ .../resources/assets/rainglow/lang/en_us.json | 4 +-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/ix0rai/rainglow/Rainglow.java b/src/main/java/io/ix0rai/rainglow/Rainglow.java index e5b5e53..7999547 100644 --- a/src/main/java/io/ix0rai/rainglow/Rainglow.java +++ b/src/main/java/io/ix0rai/rainglow/Rainglow.java @@ -18,6 +18,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + public class Rainglow implements ModInitializer { public static final String MOD_ID = "rainglow"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); @@ -27,10 +30,12 @@ public class Rainglow implements ModInitializer { public static final Gson GSON = new Gson(); public static final String CUSTOM_NBT_KEY = "Colour"; + public static final Identifier SERVER_MODE_DATA_ID = id("server_mode_data"); + public static final List RAINGLOW_DATAPACKS = new ArrayList<>(); @Override public void onInitialize() { - ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener((RainglowResourceReloader) () -> id("server_mode_data")); + ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener((RainglowResourceReloader) () -> SERVER_MODE_DATA_ID); PayloadTypeRegistry.playS2C().register(RainglowNetworking.ConfigSyncPayload.PACKET_ID, RainglowNetworking.ConfigSyncPayload.PACKET_CODEC); PayloadTypeRegistry.playS2C().register(RainglowNetworking.ModeSyncPayload.PACKET_ID, RainglowNetworking.ModeSyncPayload.PACKET_CODEC); diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 902da74..4e2e459 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -15,8 +15,6 @@ import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; import net.minecraft.client.gui.widget.text.TextWidget; import net.minecraft.client.option.Option; -import net.minecraft.client.toast.SystemToast; -import net.minecraft.client.toast.Toast; import net.minecraft.text.CommonTexts; import net.minecraft.text.Style; import net.minecraft.text.Text; @@ -49,6 +47,16 @@ public RainglowConfigScreen(@Nullable Screen parent) { this.saveButton.active = false; } + private TextWidget getInfoText() { + if (MinecraftClient.getInstance().isInSingleplayer()) { + return new TextWidget(Rainglow.translatableText("config.loaded_datapacks", RainglowMode.values().size(), Rainglow.RAINGLOW_DATAPACKS.size()), this.textRenderer).setTextColor(0x00FFFF); + } else if (MinecraftClient.getInstance().world != null) { + return new TextWidget(Rainglow.translatableText("config.server_locked"), this.textRenderer).setTextColor(0xFF0000); + } else { + return new TextWidget(Rainglow.translatableText("config.no_world"), this.textRenderer).setTextColor(0xFF0000); + } + } + @Override public void init() { HeaderFooterLayoutWidget headerFooterWidget = new HeaderFooterLayoutWidget(this, 61, 33); @@ -58,9 +66,7 @@ public void init() { // header headerLayout.add(new TextWidget(TITLE, this.textRenderer), settings -> settings.alignHorizontallyCenter().alignVerticallyTop().setPadding(12)); headerLayout.add(createModeButton(), LayoutSettings::alignVerticallyBottom); - if (MinecraftClient.getInstance().world == null) { - headerLayout.add(new TextWidget(Rainglow.translatableText("config.no_world"), this.textRenderer).setTextColor(0xc21919), LayoutSettings::alignHorizontallyCenter); - } + headerLayout.add(getInfoText(), LayoutSettings::alignHorizontallyCenter); // contents LinearLayoutWidget contentLayout = LinearLayoutWidget.createVertical(); @@ -194,17 +200,7 @@ public void closeScreen() { this.isConfirming = true; this.clearAndInit(); } else { - // overrides will exist when connected to a server syncing its values - if (Rainglow.CONFIG.mode.isBeingOverridden()) { - sendConfigLockedToast(); - } - MinecraftClient.getInstance().setScreen(this.parent); } } - - private static void sendConfigLockedToast() { - Toast toast = new SystemToast(SystemToast.Id.PACK_LOAD_FAILURE, Rainglow.translatableText("config.server_locked_title"), Rainglow.translatableText("config.server_locked_description")); - MinecraftClient.getInstance().getToastManager().add(toast); - } } diff --git a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java index fd4ee44..1fea62d 100644 --- a/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java +++ b/src/main/java/io/ix0rai/rainglow/data/RainglowResourceReloader.java @@ -24,6 +24,7 @@ default void reload(ResourceManager manager) { // this only clears modes that exist on both the server and the client // otherwise we would have to re-request the mode data packet on every reload RainglowMode.clearUniversalModes(); + Rainglow.RAINGLOW_DATAPACKS.clear(); // load custom modes from rainglow/custom_modes in the datapack // we only load files whose name ends with .json @@ -36,6 +37,11 @@ default void reload(ResourceManager manager) { Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); RainglowMode.JsonMode result = Rainglow.GSON.fromJson(reader, RainglowMode.JsonMode.class); RainglowMode.addMode(new RainglowMode(result, true)); + + String name = entry.getValue().getSourceName(); + if (this.getFabricId().equals(Rainglow.SERVER_MODE_DATA_ID) && !Rainglow.RAINGLOW_DATAPACKS.contains(name)) { + Rainglow.RAINGLOW_DATAPACKS.add(name); + } } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index e55c270..c213709 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -10,13 +10,13 @@ "rainglow.config.enable_glow_squid": "Rainbow squids", "rainglow.config.enable_slime": "Rainbow slimes", "rainglow.config.enable_allay": "Rainbow allays", - "rainglow.config.server_locked_title": "Config is locked by server", - "rainglow.config.server_locked_description": "Changes applied will only be visible once you join a world without a locked config.", + "rainglow.config.server_locked": "Config is server-locked: these values are your local config.", "rainglow.config.no_custom_colours": "Cannot save with no colours selected!", "rainglow.config.no_custom_colours_description": "Please select at least one colour to save the settings.", "rainglow.config.no_world": "No world loaded: only default modes are available.", "rainglow.config.unsaved_warning": "Config is not saved and changes will be lost! Leave without saving?", "rainglow.config.continue_editing": "Continue editing", + "rainglow.config.loaded_datapacks": "Loaded %s modes from %s datapacks.", "rainglow.config.slime_rarity.value": "Slime rarity: %s", "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.tooltip.entity_toggle": "Enable or disable rainbow colours for this entity.", From 7f7bfbe5924d0e3ff65284dc37394c72019a434b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 18:03:22 -0500 Subject: [PATCH 38/40] better --- .../java/io/ix0rai/rainglow/config/RainglowConfigScreen.java | 2 +- src/main/resources/assets/rainglow/lang/en_us.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index 4e2e459..c5603d8 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -49,7 +49,7 @@ public RainglowConfigScreen(@Nullable Screen parent) { private TextWidget getInfoText() { if (MinecraftClient.getInstance().isInSingleplayer()) { - return new TextWidget(Rainglow.translatableText("config.loaded_datapacks", RainglowMode.values().size(), Rainglow.RAINGLOW_DATAPACKS.size()), this.textRenderer).setTextColor(0x00FFFF); + return new TextWidget((Rainglow.RAINGLOW_DATAPACKS.size() == 1 ? Rainglow.translatableText("config.loaded_builtin", RainglowMode.values().size()) : Rainglow.translatableText("config.loaded_datapacks", RainglowMode.values().size(), Rainglow.RAINGLOW_DATAPACKS.size())), this.textRenderer).setTextColor(0x00FFFF); } else if (MinecraftClient.getInstance().world != null) { return new TextWidget(Rainglow.translatableText("config.server_locked"), this.textRenderer).setTextColor(0xFF0000); } else { diff --git a/src/main/resources/assets/rainglow/lang/en_us.json b/src/main/resources/assets/rainglow/lang/en_us.json index c213709..9904bc3 100644 --- a/src/main/resources/assets/rainglow/lang/en_us.json +++ b/src/main/resources/assets/rainglow/lang/en_us.json @@ -17,6 +17,7 @@ "rainglow.config.unsaved_warning": "Config is not saved and changes will be lost! Leave without saving?", "rainglow.config.continue_editing": "Continue editing", "rainglow.config.loaded_datapacks": "Loaded %s modes from %s datapacks.", + "rainglow.config.loaded_builtin": "Loaded %s built-in modes.", "rainglow.config.slime_rarity.value": "Slime rarity: %s", "rainglow.tooltip.rarity": "Rarity determines what percent chance mobs have to spawn with a colour.", "rainglow.tooltip.entity_toggle": "Enable or disable rainbow colours for this entity.", From 12141281cce0d1007bf76cae0a2ba5cbbbef65af Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 18:56:22 -0500 Subject: [PATCH 39/40] add exit confirmation to custom mode screen @orifu --- .../rainglow/config/CustomModeScreen.java | 39 +++++++++++++++---- .../rainglow/config/RainglowConfigScreen.java | 24 ++++++------ .../config/ScreenWithUnsavedWarning.java | 32 +++++++++++++++ 3 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 src/main/java/io/ix0rai/rainglow/config/ScreenWithUnsavedWarning.java diff --git a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java index 95a7a25..02df6b1 100644 --- a/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/CustomModeScreen.java @@ -19,9 +19,10 @@ import java.util.ArrayList; import java.util.List; -public class CustomModeScreen extends GameOptionsScreen { +public class CustomModeScreen extends GameOptionsScreen implements ScreenWithUnsavedWarning { private final ButtonWidget saveButton; private final List> options = new ArrayList<>(); + private boolean isConfirming; private static final Text TITLE = Rainglow.translatableText("config.custom"); @@ -79,13 +80,17 @@ public void init() { HeaderFooterLayoutWidget headerFooterWidget = new HeaderFooterLayoutWidget(this, 61, 33); headerFooterWidget.addToHeader(new TextWidget(TITLE, this.textRenderer), settings -> settings.alignHorizontallyCenter().setBottomPadding(28)); - ButtonListWidget buttonListWidget = headerFooterWidget.addToContents(new ButtonListWidget(this.client, this.width, this.height, this)); - createColourToggles(); - buttonListWidget.addEntries(this.options.toArray(new Option[0])); + if (!this.isConfirming) { + ButtonListWidget buttonListWidget = headerFooterWidget.addToContents(new ButtonListWidget(this.client, this.width, this.height, this)); + createColourToggles(); + buttonListWidget.addEntries(this.options.toArray(new Option[0])); - LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); - linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); - linearLayout.add(this.saveButton); + LinearLayoutWidget linearLayout = headerFooterWidget.addToFooter(LinearLayoutWidget.createHorizontal().setSpacing(8)); + linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); + linearLayout.add(this.saveButton); + } else { + this.setUpUnsavedWarning(headerFooterWidget, this.textRenderer, this.parent); + } headerFooterWidget.visitWidgets(this::addDrawableSelectableElement); headerFooterWidget.arrangeElements(); @@ -95,4 +100,24 @@ private static void sendNoColoursToast() { Toast toast = new SystemToast(SystemToast.Id.PACK_LOAD_FAILURE, Rainglow.translatableText("config.no_custom_colours"), Rainglow.translatableText("config.no_custom_colours_description")); MinecraftClient.getInstance().getToastManager().add(toast); } + + @Override + public void setConfirming(boolean confirming) { + this.isConfirming = confirming; + } + + @Override + public void clearAndInit() { + super.clearAndInit(); + } + + @Override + public void closeScreen() { + if (this.saveButton.active) { + this.isConfirming = true; + this.clearAndInit(); + } else { + MinecraftClient.getInstance().setScreen(this.parent); + } + } } diff --git a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java index c5603d8..18dbbe8 100644 --- a/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java +++ b/src/main/java/io/ix0rai/rainglow/config/RainglowConfigScreen.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; -public class RainglowConfigScreen extends Screen { +public class RainglowConfigScreen extends Screen implements ScreenWithUnsavedWarning { private static final Text TITLE = Rainglow.translatableText("config.title"); public static final Text YES = Text.translatable("gui.yes").styled(style -> style.withColor(0x00FF00)); public static final Text NO = Text.translatable("gui.no").styled(style -> style.withColor(0xFF0000)); @@ -93,17 +93,7 @@ public void init() { linearLayout.add(ButtonWidget.builder(CommonTexts.DONE, button -> this.closeScreen()).build()); linearLayout.add(this.saveButton); } else { - LinearLayoutWidget contentWidget = headerFooterWidget.addToContents(new LinearLayoutWidget(250, 100, LinearLayoutWidget.Orientation.VERTICAL).setSpacing(8)); - contentWidget.add(new TextWidget(Rainglow.translatableText("config.unsaved_warning"), this.textRenderer), LayoutSettings::alignHorizontallyCenter); - - LinearLayoutWidget buttons = new LinearLayoutWidget(250, 20, LinearLayoutWidget.Orientation.HORIZONTAL).setSpacing(8); - buttons.add(ButtonWidget.builder(Rainglow.translatableText("config.continue_editing"), (buttonWidget) -> { - this.isConfirming = false; - this.clearAndInit(); - }).build()); - buttons.add(ButtonWidget.builder(CommonTexts.YES, (buttonWidget) -> MinecraftClient.getInstance().setScreen(this.parent)).build()); - - contentWidget.add(buttons, LayoutSettings::alignHorizontallyCenter); + this.setUpUnsavedWarning(headerFooterWidget, this.textRenderer, this.parent); } headerFooterWidget.visitWidgets(this::addDrawableSelectableElement); @@ -203,4 +193,14 @@ public void closeScreen() { MinecraftClient.getInstance().setScreen(this.parent); } } + + @Override + public void setConfirming(boolean confirming) { + this.isConfirming = confirming; + } + + @Override + public void clearAndInit() { + super.clearAndInit(); + } } diff --git a/src/main/java/io/ix0rai/rainglow/config/ScreenWithUnsavedWarning.java b/src/main/java/io/ix0rai/rainglow/config/ScreenWithUnsavedWarning.java new file mode 100644 index 0000000..389d003 --- /dev/null +++ b/src/main/java/io/ix0rai/rainglow/config/ScreenWithUnsavedWarning.java @@ -0,0 +1,32 @@ +package io.ix0rai.rainglow.config; + +import io.ix0rai.rainglow.Rainglow; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.button.ButtonWidget; +import net.minecraft.client.gui.widget.layout.HeaderFooterLayoutWidget; +import net.minecraft.client.gui.widget.layout.LayoutSettings; +import net.minecraft.client.gui.widget.layout.LinearLayoutWidget; +import net.minecraft.client.gui.widget.text.TextWidget; +import net.minecraft.text.CommonTexts; + +public interface ScreenWithUnsavedWarning { + void setConfirming(boolean confirming); + + void clearAndInit(); + + default void setUpUnsavedWarning(HeaderFooterLayoutWidget headerFooterWidget, TextRenderer renderer, Screen parent) { + LinearLayoutWidget contentWidget = headerFooterWidget.addToContents(new LinearLayoutWidget(250, 100, LinearLayoutWidget.Orientation.VERTICAL).setSpacing(8)); + contentWidget.add(new TextWidget(Rainglow.translatableText("config.unsaved_warning"), renderer), LayoutSettings::alignHorizontallyCenter); + + LinearLayoutWidget buttons = new LinearLayoutWidget(250, 20, LinearLayoutWidget.Orientation.HORIZONTAL).setSpacing(8); + buttons.add(ButtonWidget.builder(Rainglow.translatableText("config.continue_editing"), (buttonWidget) -> { + this.setConfirming(false); + this.clearAndInit(); + }).build()); + buttons.add(ButtonWidget.builder(CommonTexts.YES, (buttonWidget) -> MinecraftClient.getInstance().setScreen(parent)).build()); + + contentWidget.add(buttons, LayoutSettings::alignHorizontallyCenter); + } +} From 26fd63f07dbb00b406a72678a92ad9ef6c3fb426 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 25 May 2024 19:17:32 -0500 Subject: [PATCH 40/40] fix contact metadata @orifu --- src/main/resources/fabric.mod.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e7e8104..e183bb0 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -11,8 +11,9 @@ ], "contact": { - "website": "https://modrinth.com/mod/rainglow", - "repo": "https://github.com/ix0rai/rainglow" + "homepage": "https://modrinth.com/mod/rainglow", + "sources": "https://github.com/ix0rai/rainglow", + "issues": "https://github.com/ix0rai/rainglow/issues" }, "license": "MIT",