Puzzle 2.0.0 - Multiplatform & Cleaner code

- Removed intrusive branding (previously visible on Title screen and F3 menu)
- De-modularized: Previously, Puzzle was split into multiple modules which complicated the development process and was just unnecessary
- Experimental NeoForge support
- Many small improvements
- puzzle-splashscreen: Improved  background image blending
- puzzle-splashscreen: Added support for custom blend functions for full OptiFine parity
This commit is contained in:
Martin Prokoph
2024-09-06 18:44:27 +02:00
parent 651483af22
commit 35c613a536
80 changed files with 926 additions and 1063 deletions

View File

@@ -0,0 +1,18 @@
package net.puzzlemc.core;
import net.puzzlemc.core.config.PuzzleConfig;
import net.puzzlemc.gui.PuzzleGui;
import net.puzzlemc.splashscreen.PuzzleSplashScreen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PuzzleCore {
public final static String MOD_ID = "puzzle";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static void initModules() {
PuzzleConfig.init(MOD_ID, PuzzleConfig.class);
PuzzleGui.init();
PuzzleSplashScreen.init();
}
}

View File

@@ -0,0 +1,28 @@
package net.puzzlemc.core.config;
import eu.midnightdust.lib.config.MidnightConfig;
import java.util.ArrayList;
import java.util.List;
public class PuzzleConfig extends MidnightConfig {
private static final String GUI = "gui";
private static final String INTERNAL = "internal";
private static final String FEATURES = "features";
@Entry(category = GUI, name = "Disabled integrations") public static List<String> disabledIntegrations = new ArrayList<>();
@Entry(category = GUI, name = "Enable Puzzle button") public static boolean enablePuzzleButton = true;
@Entry(category = FEATURES, name = "puzzle.option.resourcepack_splash_screen") public static boolean resourcepackSplashScreen = true;
@Entry(category = FEATURES, name = "puzzle.option.unlimited_model_rotations") public static boolean unlimitedRotations = true;
@Entry(category = FEATURES, name = "puzzle.option.bigger_custom_models") public static boolean biggerModels = true;
@Entry(category = INTERNAL, name = "Enable debug messages") public static boolean debugMessages = false;
@Entry(category = INTERNAL, name = "Has custom splash screen") public static boolean hasCustomSplashScreen = false;
@Entry(category = INTERNAL, name = "Splash Background Color") public static int backgroundColor = 15675965;
@Entry(category = INTERNAL, name = "Splash Progress Bar Color") public static int progressBarColor = 16777215;
@Entry(category = INTERNAL, name = "Splash Progress Bar Background Color") public static int progressBarBackgroundColor = 15675965;
@Entry(category = INTERNAL, name = "Splash Progress Bar Frame Color") public static int progressFrameColor = 16777215;
@Entry(category = INTERNAL, name = "puzzle.option.better_splash_screen_blend") public static boolean disableBlend = false;
@Entry(category = INTERNAL, name = "Custom Blend Function") public static List<String> customBlendFunction = new ArrayList<>();
}

View File

@@ -0,0 +1,37 @@
package net.puzzlemc.gui;
import net.puzzlemc.core.config.PuzzleConfig;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import java.util.ArrayList;
import java.util.List;
import static net.puzzlemc.core.PuzzleCore.LOGGER;
public class PuzzleApi {
public static List<PuzzleWidget> GRAPHICS_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> MISC_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> PERFORMANCE_OPTIONS = new ArrayList<>();
public static List<PuzzleWidget> RESOURCE_OPTIONS = new ArrayList<>();
public static void addToGraphicsOptions(PuzzleWidget button) {
GRAPHICS_OPTIONS.add(button);
if (PuzzleConfig.debugMessages)
LOGGER.info("{} -> Graphics Options", button.descriptionText.getContent().toString());
}
public static void addToMiscOptions(PuzzleWidget button) {
MISC_OPTIONS.add(button);
if (PuzzleConfig.debugMessages)
LOGGER.info("{} -> Misc Options", button.descriptionText.getContent().toString());
}
public static void addToPerformanceOptions(PuzzleWidget button) {
PERFORMANCE_OPTIONS.add(button);
if (PuzzleConfig.debugMessages)
LOGGER.info("{}- > Performance Options", button.descriptionText.getContent().toString());
}
public static void addToResourceOptions(PuzzleWidget button) {
RESOURCE_OPTIONS.add(button);
if (PuzzleConfig.debugMessages)
LOGGER.info("{} -> Resource Options", button.descriptionText.getContent().toString());
}
}

View File

@@ -0,0 +1,68 @@
package net.puzzlemc.gui;
import eu.midnightdust.core.MidnightLib;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.util.Identifier;
import net.puzzlemc.core.config.PuzzleConfig;
import net.puzzlemc.gui.compat.*;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.puzzlemc.splashscreen.PuzzleSplashScreen;
import static net.puzzlemc.core.PuzzleCore.LOGGER;
public class PuzzleGui {
public final static String id = "puzzle";
public static final Text YES = Text.translatable("gui.yes").formatted(Formatting.GREEN);
public static final Text NO = Text.translatable("gui.no").formatted(Formatting.RED);
public static final Identifier PUZZLE_BUTTON = Identifier.of(id, "icon/button");
public static void init() {
MidnightLib.hiddenMods.add("puzzle");
MinecraftClient client = MinecraftClient.getInstance();
PuzzleApi.addToMiscOptions(new PuzzleWidget(Text.of("Puzzle")));
PuzzleApi.addToMiscOptions(new PuzzleWidget(Text.translatable("puzzle.midnightconfig.title"), (button) -> button.setMessage(Text.of("OPEN")), (button) -> {
client.setScreen(PuzzleConfig.getScreen(client.currentScreen, "puzzle"));
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.of("Puzzle")));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("puzzle.option.resourcepack_splash_screen"), (button) -> button.setMessage(PuzzleConfig.resourcepackSplashScreen ? YES : NO), (button) -> {
PuzzleConfig.resourcepackSplashScreen = !PuzzleConfig.resourcepackSplashScreen;
PuzzleSplashScreen.resetColors();
PuzzleConfig.write(id);
MinecraftClient.getInstance().getTextureManager().registerTexture(PuzzleSplashScreen.LOGO, new PuzzleSplashScreen.LogoTexture(PuzzleSplashScreen.LOGO));
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("puzzle.option.unlimited_model_rotations"), (button) -> button.setMessage(PuzzleConfig.unlimitedRotations ? YES : NO), (button) -> {
PuzzleConfig.unlimitedRotations = !PuzzleConfig.unlimitedRotations;
PuzzleConfig.write(id);
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("puzzle.option.bigger_custom_models"), (button) -> button.setMessage(PuzzleConfig.biggerModels ? YES : NO), (button) -> {
PuzzleConfig.biggerModels = !PuzzleConfig.biggerModels;
PuzzleConfig.write(id);
}));
if (isActive("cullleaves")) CullLeavesCompat.init();
if (isActive("colormatic")) ColormaticCompat.init();
if (isActive("borderlessmining")) BorderlessMiningCompat.init();
if (isActive("iris")) IrisCompat.init();
}
public static boolean lateInitDone = false;
public static void lateInit() { // Some mods are initialized after Puzzle, so we can't access them in our ClientModInitializer
if (isActive("lambdynlights")) LDLCompat.init();
if (isActive("citresewn")) CITRCompat.init();
if (isActive("lambdabettergrass")) LBGCompat.init();
if (isActive("continuity")) ContinuityCompat.init();
try {
if (isActive("entity_texture_features")) ETFCompat.init();
if (isActive("entity_model_features")) EMFCompat.init();
} catch (Exception e) {
LOGGER.error("ETF/EMF config structure changed. Again...", e);
}
lateInitDone = true;
}
public static boolean isActive(String modid) {
return PlatformFunctions.isModLoaded(modid) && !PuzzleConfig.disabledIntegrations.contains(modid);
}
}

View File

@@ -0,0 +1,28 @@
package net.puzzlemc.gui.compat;
import link.infra.borderlessmining.config.ConfigHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class BorderlessMiningCompat {
public static void init() {
PuzzleApi.addToMiscOptions(new PuzzleWidget(Text.of("Borderless Mining")));
ConfigHandler bmConfig = ConfigHandler.getInstance();
PuzzleApi.addToMiscOptions(new PuzzleWidget(Text.translatable("config.borderlessmining.general.enabled"), (button) -> button.setMessage(bmConfig.isEnabledOrPending() ? YES : NO), (button) -> {
bmConfig.setEnabledPending(!bmConfig.isEnabledOrPending());
bmConfig.save();
}));
if (MinecraftClient.IS_SYSTEM_MAC) {
PuzzleApi.addToMiscOptions(new PuzzleWidget(Text.translatable("config.borderlessmining.general.enabledmac"), (button) -> button.setMessage(bmConfig.enableMacOS ? YES : NO), (button) -> {
bmConfig.enableMacOS = !bmConfig.enableMacOS;
bmConfig.setEnabledPending(bmConfig.isEnabled());
bmConfig.save();
}));
}
}
}

View File

@@ -0,0 +1,60 @@
package net.puzzlemc.gui.compat;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import shcm.shsupercm.fabric.citresewn.config.CITResewnConfig;
import static net.minecraft.screen.ScreenTexts.NO;
import static net.minecraft.screen.ScreenTexts.YES;
public class CITRCompat {
public static void init() {
if (CITResewnConfig.INSTANCE != null) {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.of("CIT Resewn")));
CITResewnConfig citConfig = CITResewnConfig.INSTANCE;
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.citresewn.enabled.title"), (button) -> button.setMessage(citConfig.enabled ? YES : NO), (button) -> {
citConfig.enabled = !citConfig.enabled;
citConfig.write();
MinecraftClient.getInstance().reloadResources();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.citresewn.mute_errors.title"), (button) -> button.setMessage(citConfig.mute_errors ? YES : NO), (button) -> {
citConfig.mute_errors = !citConfig.mute_errors;
citConfig.write();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.citresewn.mute_warns.title"), (button) -> button.setMessage(citConfig.mute_warns ? YES : NO), (button) -> {
citConfig.mute_warns = !citConfig.mute_warns;
citConfig.write();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.citresewn.broken_paths.title"), (button) -> button.setMessage(citConfig.broken_paths ? YES : NO), (button) -> {
citConfig.broken_paths = !citConfig.broken_paths;
citConfig.write();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(0, 100, Text.translatable("config.citresewn.cache_ms.title"), () -> citConfig.cache_ms,
(button) -> button.setMessage(message(citConfig)),
(slider) -> {
try {
citConfig.cache_ms = slider.getInt();
} catch (NumberFormatException ignored) {
}
citConfig.write();
}));
}
}
public static Text message(CITResewnConfig config) {
int ticks = config.cache_ms;
if (ticks <= 1) {
return (Text.translatable("config.citresewn.cache_ms.ticks." + ticks)).formatted(Formatting.AQUA);
} else {
Formatting color = Formatting.DARK_RED;
if (ticks <= 40) color = Formatting.RED;
if (ticks <= 20) color = Formatting.GOLD;
if (ticks <= 10) color = Formatting.DARK_GREEN;
if (ticks <= 5) color = Formatting.GREEN;
return (Text.translatable("config.citresewn.cache_ms.ticks.any", ticks)).formatted(color);
}
}
}

View File

@@ -0,0 +1,46 @@
package net.puzzlemc.gui.compat;
import io.github.kvverti.colormatic.Colormatic;
import io.github.kvverti.colormatic.ColormaticConfig;
import io.github.kvverti.colormatic.ColormaticConfigController;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class ColormaticCompat {
public static void init() {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.of("Colormatic")));
ColormaticConfig colormaticConfig = Colormatic.config();
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("colormatic.config.option.clearSky"), (button) -> button.setMessage(colormaticConfig.clearSky ? YES : NO), (button) -> {
colormaticConfig.clearSky = !colormaticConfig.clearSky;
ColormaticConfigController.persist(colormaticConfig);
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("colormatic.config.option.clearVoid"), (button) -> button.setMessage(colormaticConfig.clearVoid ? YES : NO), (button) -> {
colormaticConfig.clearVoid = !colormaticConfig.clearVoid;
ColormaticConfigController.persist(colormaticConfig);
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("colormatic.config.option.blendSkyLight"), (button) -> button.setMessage(colormaticConfig.blendSkyLight ? YES : NO), (button) -> {
colormaticConfig.blendSkyLight = !colormaticConfig.blendSkyLight;
ColormaticConfigController.persist(colormaticConfig);
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("colormatic.config.option.flickerBlockLight"), (button) -> button.setMessage(colormaticConfig.flickerBlockLight ? YES : NO), (button) -> {
colormaticConfig.flickerBlockLight = !colormaticConfig.flickerBlockLight;
ColormaticConfigController.persist(colormaticConfig);
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(0, 100, Text.translatable("colormatic.config.option.relativeBlockLightIntensity"),
() -> (int) (100*(1.0 - colormaticConfig.relativeBlockLightIntensityExponent / -16.0)),
(button) -> button.setMessage(Text.translatable("colormatic.config.option.relativeBlockLightIntensity")
.append(": ")
.append(Text.literal(String.valueOf((int)(100 * Math.exp(ColormaticConfig.scaled(colormaticConfig.relativeBlockLightIntensityExponent))))).append("%"))),
(slider) -> {
try {
colormaticConfig.relativeBlockLightIntensityExponent = ((1.00 - ((slider.getInt())/100f)) * -16.0);
ColormaticConfigController.persist(colormaticConfig);
}
catch (NumberFormatException ignored) {}
}));
}
}

View File

@@ -0,0 +1,27 @@
package net.puzzlemc.gui.compat;
import me.pepperbell.continuity.client.config.ContinuityConfig;
import me.pepperbell.continuity.client.config.Option;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class ContinuityCompat {
public static void init() {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.of("Continuity")));
ContinuityConfig contConfig = ContinuityConfig.INSTANCE;
contConfig.getOptionMapView().forEach((s, option) -> {
if (s.equals("use_manual_culling")) return;
try {
Option.BooleanOption booleanOption = ((Option.BooleanOption)option);
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("options.continuity."+s), (button) -> button.setMessage(booleanOption.get() ? YES : NO), (button) -> {
booleanOption.set(!booleanOption.get());
contConfig.save();
}));
} catch (Exception ignored) {}
});
}
}

View File

@@ -0,0 +1,21 @@
package net.puzzlemc.gui.compat;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class CullLeavesCompat {
public static void init() {
PuzzleApi.addToPerformanceOptions(new PuzzleWidget(Text.of("CullLeaves")));
PuzzleApi.addToPerformanceOptions(new PuzzleWidget(Text.translatable("cullleaves.puzzle.option.enabled"), (button) -> button.setMessage(CullLeavesConfig.enabled ? YES : NO), (button) -> {
CullLeavesConfig.enabled = !CullLeavesConfig.enabled;
CullLeavesConfig.write("cullleaves");
MinecraftClient.getInstance().worldRenderer.reload();
}));
}
}

View File

@@ -0,0 +1,39 @@
package net.puzzlemc.gui.compat;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import traben.entity_model_features.EMF;
import traben.entity_model_features.config.EMFConfig;
import java.util.EnumSet;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeSet;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class EMFCompat {
public static void init() {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("entity_model_features.title")));
EMFConfig emfConfig = EMF.config().getConfig();
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("entity_model_features.config.force_models"), (button) -> button.setMessage(emfConfig.attemptRevertingEntityModelsAlteredByAnotherMod ? YES : NO), (button) -> {
emfConfig.attemptRevertingEntityModelsAlteredByAnotherMod = !emfConfig.attemptRevertingEntityModelsAlteredByAnotherMod;
EMF.config().saveToFile();
}));
if (PlatformFunctions.isModLoaded("physicsmod")) {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("entity_model_features.config.physics"), (button) -> button.setMessage(emfConfig.attemptPhysicsModPatch_2 != EMFConfig.PhysicsModCompatChoice.OFF ?
Text.translatable("entity_model_features.config." + (emfConfig.attemptPhysicsModPatch_2 == EMFConfig.PhysicsModCompatChoice.VANILLA ? "physics.1" : "physics.2")) : ScreenTexts.OFF), (button) -> {
final NavigableSet<EMFConfig.PhysicsModCompatChoice> set =
new TreeSet<>(EnumSet.allOf(EMFConfig.PhysicsModCompatChoice.class));
emfConfig.attemptPhysicsModPatch_2 = Objects.requireNonNullElseGet(
set.higher(emfConfig.attemptPhysicsModPatch_2), set::first);
EMF.config().saveToFile();
}));
}
}
}

View File

@@ -0,0 +1,47 @@
package net.puzzlemc.gui.compat;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import traben.entity_texture_features.ETFApi;
import traben.entity_texture_features.config.ETFConfig;
import java.util.EnumSet;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeSet;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class ETFCompat {
public static void init() {
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.title")));
ETFConfig etfConfig = ETFApi.getETFConfigObject();
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.enable_custom_textures.title"), (button) -> button.setMessage(etfConfig.enableCustomTextures ? YES : NO), (button) -> {
etfConfig.enableCustomTextures = !etfConfig.enableCustomTextures;
ETFApi.saveETFConfigChangesAndResetETF();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.enable_emissive_textures.title"), (button) -> button.setMessage(etfConfig.enableEmissiveTextures ? YES : NO), (button) -> {
etfConfig.enableEmissiveTextures = !etfConfig.enableEmissiveTextures;
ETFApi.saveETFConfigChangesAndResetETF();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.emissive_mode.title"), (button) -> button.setMessage(
Text.literal(etfConfig.emissiveRenderMode.toString())), (button) -> {
final NavigableSet<ETFConfig.EmissiveRenderModes> set =
new TreeSet<>(EnumSet.allOf(ETFConfig.EmissiveRenderModes.class));
etfConfig.emissiveRenderMode = Objects.requireNonNullElseGet(
set.higher(etfConfig.emissiveRenderMode), set::first);
ETFApi.saveETFConfigChangesAndResetETF();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.blinking_mob_settings.title"), (button) -> button.setMessage(etfConfig.enableBlinking ? YES : NO), (button) -> {
etfConfig.enableBlinking = !etfConfig.enableBlinking;
ETFApi.saveETFConfigChangesAndResetETF();
}));
PuzzleApi.addToResourceOptions(new PuzzleWidget(Text.translatable("config.entity_texture_features.player_skin_features.title"), (button) -> button.setMessage(etfConfig.skinFeaturesEnabled ? YES : NO), (button) -> {
etfConfig.skinFeaturesEnabled = !etfConfig.skinFeaturesEnabled;
ETFApi.saveETFConfigChangesAndResetETF();
}));
}
}

View File

@@ -0,0 +1,25 @@
package net.puzzlemc.gui.compat;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisApiConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.PuzzleGui;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
public class IrisCompat {
public static void init() {
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.of("Iris")));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.translatable("iris.puzzle.option.enableShaders"), (button) -> button.setMessage(IrisApi.getInstance().getConfig().areShadersEnabled() ? PuzzleGui.YES : PuzzleGui.NO), (button) -> {
IrisApiConfig irisConfig = IrisApi.getInstance().getConfig();
irisConfig.setShadersEnabledAndApply(!irisConfig.areShadersEnabled());
}));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.translatable("options.iris.shaderPackSelection.title"), (button) -> button.setMessage(Text.literal("").append(Text.translatable("iris.puzzle.option.open").formatted(Formatting.GOLD))), (button) -> {
MinecraftClient client = MinecraftClient.getInstance();
client.setScreen((Screen) IrisApi.getInstance().openMainIrisScreenObj(client.currentScreen));
}));
}
}

View File

@@ -0,0 +1,19 @@
package net.puzzlemc.gui.compat;
import dev.lambdaurora.lambdabettergrass.LBGConfig;
import dev.lambdaurora.lambdabettergrass.LambdaBetterGrass;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class LBGCompat {
public static void init() {
LBGConfig lbgConfig = LambdaBetterGrass.get().config;
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.of("LambdaBetterGrass")));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.translatable("lambdabettergrass.option.mode"), (button) -> button.setMessage(lbgConfig.getMode().getTranslatedText()), (button) -> lbgConfig.setMode(lbgConfig.getMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.translatable("lambdabettergrass.option.better_snow"), (button) -> button.setMessage(lbgConfig.hasBetterLayer() ? YES : NO), (button) -> lbgConfig.setBetterLayer(!lbgConfig.hasBetterLayer())));
}
}

View File

@@ -0,0 +1,24 @@
package net.puzzlemc.gui.compat;
import dev.lambdaurora.lambdynlights.DynamicLightsConfig;
import dev.lambdaurora.lambdynlights.LambDynLights;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.screen.widget.PuzzleWidget;
import static net.puzzlemc.gui.PuzzleGui.NO;
import static net.puzzlemc.gui.PuzzleGui.YES;
public class LDLCompat {
public static void init() {
DynamicLightsConfig ldlConfig = LambDynLights.get().config;
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.of("LambDynamicLights")));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.translatable("lambdynlights.option.mode"), (button) -> button.setMessage(ldlConfig.getDynamicLightsMode().getTranslatedText()), (button) -> ldlConfig.setDynamicLightsMode(ldlConfig.getDynamicLightsMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.empty().append("DynLights: ").append(Text.translatable("lambdynlights.option.light_sources.entities")), (button) -> button.setMessage(ldlConfig.getEntitiesLightSource().get() ? YES : NO), (button) -> ldlConfig.getEntitiesLightSource().set(!ldlConfig.getEntitiesLightSource().get())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.empty().append("DynLights: ").append(Text.translatable("lambdynlights.option.light_sources.block_entities")), (button) -> button.setMessage(ldlConfig.getBlockEntitiesLightSource().get() ? YES : NO), (button) -> ldlConfig.getBlockEntitiesLightSource().set(!ldlConfig.getBlockEntitiesLightSource().get())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.empty().append("DynLights: ").append(Text.translatable("entity.minecraft.creeper")), (button) -> button.setMessage(ldlConfig.getCreeperLightingMode().getTranslatedText()), (button) -> ldlConfig.setCreeperLightingMode(ldlConfig.getCreeperLightingMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.empty().append("DynLights: ").append(Text.translatable("block.minecraft.tnt")), (button) -> button.setMessage(ldlConfig.getTntLightingMode().getTranslatedText()), (button) -> ldlConfig.setTntLightingMode(ldlConfig.getTntLightingMode().next())));
PuzzleApi.addToGraphicsOptions(new PuzzleWidget(Text.empty().append("DynLights: ").append(Text.translatable("lambdynlights.option.light_sources.water_sensitive_check")), (button) -> button.setMessage(ldlConfig.getWaterSensitiveCheck().get() ? YES : NO), (button) -> ldlConfig.getWaterSensitiveCheck().set(!ldlConfig.getWaterSensitiveCheck().get())));
}
}

View File

@@ -0,0 +1,52 @@
package net.puzzlemc.gui.mixin;
import eu.midnightdust.core.config.MidnightLibConfig;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.gui.widget.ThreePartsLayoutWidget;
import net.puzzlemc.core.config.PuzzleConfig;
import net.puzzlemc.gui.PuzzleGui;
import net.puzzlemc.gui.screen.PuzzleOptionsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.option.OptionsScreen;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
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.callback.CallbackInfo;
import java.util.Objects;
@Mixin(OptionsScreen.class)
public abstract class MixinOptionsScreen extends Screen {
@Shadow @Final private ThreePartsLayoutWidget layout;
@Unique TextIconButtonWidget puzzle$button = TextIconButtonWidget.builder(Text.translatable("puzzle.screen.title"), (buttonWidget) ->
(Objects.requireNonNull(this.client)).setScreen(new PuzzleOptionsScreen(this)), true)
.dimension(20, 20).texture(PuzzleGui.PUZZLE_BUTTON, 20, 20).build();
private MixinOptionsScreen(Text title) {super(title);}
@Inject(at = @At("HEAD"), method = "init")
public void puzzle$onInit(CallbackInfo ci) {
if (PuzzleConfig.enablePuzzleButton) {
this.puzzle$setButtonPos();
this.addDrawableChild(puzzle$button);
}
}
@Inject(at = @At("TAIL"), method = "initTabNavigation")
public void puzzle$onResize(CallbackInfo ci) {
if (PuzzleConfig.enablePuzzleButton) this.puzzle$setButtonPos();
}
@Unique
public void puzzle$setButtonPos() {
int i = 0;
if (PlatformFunctions.isModLoaded("lod")) i = i + 358;
if (MidnightLibConfig.config_screen_list.equals(MidnightLibConfig.ConfigButton.FALSE)) i = i - 25;
puzzle$button.setPosition(this.width / 2 - 178 + i, layout.getY() + layout.getFooterHeight() - 4);
}
}

View File

@@ -0,0 +1,92 @@
package net.puzzlemc.gui.screen;
import com.google.common.collect.Lists;
import net.minecraft.client.gui.tab.GridScreenTab;
import net.minecraft.client.gui.tab.Tab;
import net.minecraft.client.gui.tab.TabManager;
import net.minecraft.client.gui.widget.TabNavigationWidget;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import net.puzzlemc.gui.PuzzleApi;
import net.puzzlemc.gui.PuzzleGui;
import net.puzzlemc.gui.screen.widget.*;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import java.util.List;
import java.util.Objects;
public class PuzzleOptionsScreen extends Screen {
public PuzzleOptionListWidget list;
public List<Text> tooltip = null;
public TabManager tabManager = new TabManager(a -> {}, a -> {});
public Tab prevTab;
public TabNavigationWidget tabNavigation;
public static Text graphicsTab = Text.translatable("puzzle.page.graphics");
public static Text miscTab = Text.translatable("puzzle.page.misc");
public static Text performanceTab = Text.translatable("puzzle.page.performance");
public static Text resourcesTab = Text.translatable("puzzle.page.resources");
public PuzzleOptionsScreen(Screen parent) {
super(Text.translatable("puzzle.screen.title"));
this.parent = parent;
}
private final Screen parent;
@Override
protected void init() {
if (!PuzzleGui.lateInitDone) PuzzleGui.lateInit();
List<Tab> tabs = Lists.newArrayList();
if (!PuzzleApi.GRAPHICS_OPTIONS.isEmpty()) tabs.add(new GridScreenTab(graphicsTab));
if (!PuzzleApi.RESOURCE_OPTIONS.isEmpty()) tabs.add(new GridScreenTab(resourcesTab));
if (!PuzzleApi.PERFORMANCE_OPTIONS.isEmpty()) tabs.add(new GridScreenTab(performanceTab));
if (!PuzzleApi.MISC_OPTIONS.isEmpty()) tabs.add(new GridScreenTab(miscTab));
tabNavigation = TabNavigationWidget.builder(tabManager, this.width).tabs(tabs.toArray(new Tab[0])).build();
tabNavigation.selectTab(0, false);
tabNavigation.init();
prevTab = tabManager.getCurrentTab();
this.list = new PuzzleOptionListWidget(this.client, this.width, this.height - 57, 24, 25);
fillList();
if (tabs.size() > 1) {
this.addDrawableChild(tabNavigation);
list.renderHeaderSeparator = false;
}
this.addDrawableChild(list);
super.init();
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> Objects.requireNonNull(client).setScreen(parent)).dimensions(this.width / 2 - 100, this.height - 26, 200, 20).build());
}
private void fillList() {
List<PuzzleWidget> options = List.of();
if (tabManager.getCurrentTab() == null) return;
else {
Text title = tabManager.getCurrentTab().getTitle();
if (title.equals(graphicsTab)) options = PuzzleApi.GRAPHICS_OPTIONS;
else if (title.equals(miscTab)) options = PuzzleApi.MISC_OPTIONS;
else if (title.equals(performanceTab))
options = PuzzleApi.PERFORMANCE_OPTIONS;
else if (title.equals(resourcesTab)) options = PuzzleApi.RESOURCE_OPTIONS;
}
list.addAll(options);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (this.tabNavigation.trySwitchTabsWithKey(keyCode)) return true;
return super.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public void tick() {
super.tick();
if (prevTab != null && prevTab != tabManager.getCurrentTab()) {
prevTab = tabManager.getCurrentTab();
this.list.clear();
fillList();
list.setScrollAmount(0);
}
}
}

View File

@@ -0,0 +1,5 @@
package net.puzzlemc.gui.screen.widget;
public enum ButtonType {
TEXT, BUTTON, SLIDER, TEXT_FIELD
}

View File

@@ -0,0 +1,22 @@
package net.puzzlemc.gui.screen.widget;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import java.util.function.Supplier;
public class PuzzleButtonWidget extends ButtonWidget {
private final PuzzleWidget.TextAction title;
public PuzzleButtonWidget(int x, int y, int width, int height, PuzzleWidget.TextAction title, PressAction onPress) {
super(x, y, width, height, Text.of(""), onPress, Supplier::get);
this.title = title;
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
try { title.setTitle(this);
} catch (Exception e) {e.fillInStackTrace(); this.visible = false;}
super.renderWidget(context, mouseX, mouseY, delta);
}
}

View File

@@ -0,0 +1,83 @@
package net.puzzlemc.gui.screen.widget;
import eu.midnightdust.lib.config.MidnightConfig;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.Formatting;
import net.puzzlemc.gui.screen.PuzzleOptionsScreen;
import java.util.ArrayList;
import java.util.List;
import static net.puzzlemc.core.PuzzleCore.LOGGER;
@Environment(EnvType.CLIENT)
public class PuzzleOptionListWidget extends MidnightConfig.MidnightConfigListWidget {
TextRenderer textRenderer;
public PuzzleOptionListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l) {
super(minecraftClient, i, j, k, l);
this.centerListVertically = false;
textRenderer = minecraftClient.textRenderer;
}
public void addAll(List<PuzzleWidget> buttons) {
int buttonX = this.width - 160;
for (PuzzleWidget button : buttons) {
if (button.buttonType == ButtonType.TEXT)
this.addButton(List.of(), Text.literal(" ").append(button.descriptionText).formatted(Formatting.BOLD));
else if (button.buttonType == ButtonType.BUTTON)
this.addButton(List.of(new PuzzleButtonWidget(buttonX, 0, 150, 20, button.buttonTextAction, button.onPress)), button.descriptionText);
else if (button.buttonType == ButtonType.SLIDER)
this.addButton(List.of(new PuzzleSliderWidget(button.min, button.max, buttonX, 0, 150, 20, button.defaultSliderValue.getAsInt(), button.buttonTextAction, button.changeSliderValue)), button.descriptionText);
else if (button.buttonType == ButtonType.TEXT_FIELD)
this.addButton(List.of(new PuzzleTextFieldWidget(textRenderer, buttonX, 0, 150, 20, button.setTextValue, button.changeTextValue)), button.descriptionText);
else
LOGGER.warn("Button {} is missing the buttonType variable. This shouldn't happen!", button);
}
}
public void addButton(List<ClickableWidget> buttons, Text text) {
var entry = new MidnightConfig.ButtonEntry(buttons, text, new MidnightConfig.EntryInfo());
entry.centered = buttons.isEmpty();
this.addEntry(entry);
}
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
super.renderWidget(context, mouseX, mouseY, delta);
MidnightConfig.ButtonEntry e = this.getHoveredEntry();
if (client.currentScreen instanceof PuzzleOptionsScreen page && e != null && !e.buttons.isEmpty() &&
e.text.getContent() instanceof TranslatableTextContent content) {
ClickableWidget button = e.buttons.getFirst();
String key = null;
if (I18n.hasTranslation(content.getKey() + ".tooltip")) key = content.getKey() + ".tooltip";
else if (I18n.hasTranslation(content.getKey() + ".desc")) key = content.getKey() + ".desc";
if (key == null && content.getKey().endsWith(".title")) {
String strippedContent = content.getKey().substring(0, content.getKey().length()-6);
if (I18n.hasTranslation(strippedContent + ".tooltip")) key = strippedContent + ".tooltip";
else if (I18n.hasTranslation(strippedContent + ".desc")) key = strippedContent + ".desc";
}
if (key != null) {
List<Text> list = new ArrayList<>();
for (String str : I18n.translate(key).split("\n"))
list.add(Text.literal(str));
page.tooltip = list;
if (!button.isMouseOver(mouseX, mouseY)) {
context.drawTooltip(textRenderer, list, button.getX(), button.getY() + (button.getHeight() * 2));
}
else context.drawTooltip(textRenderer, list, mouseX, mouseY);
}
}
}
@Override
public MidnightConfig.ButtonEntry getHoveredEntry() {
return super.getHoveredEntry();
}
}

View File

@@ -0,0 +1,42 @@
package net.puzzlemc.gui.screen.widget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.text.Text;
public class PuzzleSliderWidget extends SliderWidget {
private final int min;
private final int max;
private final PuzzleWidget.TextAction setTextAction;
private final PuzzleWidget.ChangeSliderValueAction changeAction;
public PuzzleSliderWidget(int min, int max, int x, int y, int width, int height, float defaultValue, PuzzleWidget.TextAction setTextAction, PuzzleWidget.ChangeSliderValueAction changeAction) {
super(x,y,width,height,Text.of(""),(defaultValue-min) / (max - min));
this.min = min;
this.max = max;
this.setTextAction = setTextAction;
this.changeAction = changeAction;
try {
this.updateMessage();
} catch (Exception e) {e.fillInStackTrace(); this.visible = false;}
}
public int getInt() {
int difference = max - min;
int r = (int) (value * difference);
return r + min;
}
public void setInt(int v) {
value = value / v - value * min;
}
@Override
protected void updateMessage() {
this.setTextAction.setTitle(this);
}
@Override
protected void applyValue() {
this.changeAction.onChange(this);
}
}

View File

@@ -0,0 +1,25 @@
package net.puzzlemc.gui.screen.widget;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
public class PuzzleTextFieldWidget extends TextFieldWidget {
private final PuzzleWidget.SetTextValueAction setValueAction;
private final PuzzleWidget.ChangeTextValueAction change;
public PuzzleTextFieldWidget(TextRenderer textRenderer, int x, int y, int width, int height, PuzzleWidget.SetTextValueAction setValue, PuzzleWidget.ChangeTextValueAction change) {
super(textRenderer, x, y, width, height, Text.of(""));
this.setValueAction = setValue;
this.change = change;
try {
setValueAction.setTextValue(this);
} catch (Exception e) {e.fillInStackTrace(); this.setVisible(false);}
}
@Override
public void write(String text) {
super.write(text);
this.change.onChange(this);
setValueAction.setTextValue(this);
}
}

View File

@@ -0,0 +1,78 @@
package net.puzzlemc.gui.screen.widget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
import java.util.function.IntSupplier;
public class PuzzleWidget {
public ButtonType buttonType;
public int min;
public int max;
public Text descriptionText;
public TextAction buttonTextAction;
public ButtonWidget.PressAction onPress;
public PuzzleWidget.SetTextValueAction setTextValue;
public IntSupplier defaultSliderValue;
public PuzzleWidget.ChangeTextValueAction changeTextValue;
public PuzzleWidget.ChangeSliderValueAction changeSliderValue;
/**
* Puzzle Text Widget Container
* @param descriptionText The text you want to display.
*/
public PuzzleWidget(Text descriptionText) {
this.buttonType = ButtonType.TEXT;
this.descriptionText = descriptionText;
}
/**
* Puzzle Button Widget Container
* @param descriptionText Tells the user what the option does.
* @param getTitle Function to set the text on the button.
* @param onPress Function to call when the user presses the button.
*/
public PuzzleWidget(Text descriptionText, PuzzleWidget.TextAction getTitle, ButtonWidget.PressAction onPress) {
this.buttonType = ButtonType.BUTTON;
this.descriptionText = descriptionText;
this.buttonTextAction = getTitle;
this.onPress = onPress;
}
/**
* Puzzle Slider Widget Container
*/
public PuzzleWidget(int min, int max, Text descriptionText, IntSupplier defaultSliderValue, PuzzleWidget.TextAction setTextAction, PuzzleWidget.ChangeSliderValueAction changeAction) {
this.buttonType = ButtonType.SLIDER;
this.min = min;
this.max = max;
this.descriptionText = descriptionText;
this.defaultSliderValue = defaultSliderValue;
this.buttonTextAction = setTextAction;
this.changeSliderValue = changeAction;
}
/**
* Puzzle Text Field Widget Container (WIP - Doesn't work)
*/
public PuzzleWidget(int min, int max, Text descriptionText, PuzzleWidget.SetTextValueAction setValue, ChangeTextValueAction changeAction) {
this.buttonType = ButtonType.TEXT_FIELD;
this.min = min;
this.max = max;
this.descriptionText = descriptionText;
this.setTextValue = setValue;
this.changeTextValue = changeAction;
}
public interface ChangeTextValueAction {
void onChange(TextFieldWidget textField);
}
public interface ChangeSliderValueAction {
void onChange(PuzzleSliderWidget slider);
}
public interface SetTextValueAction {
void setTextValue(TextFieldWidget textField);
}
public interface TextAction {
void setTitle(ClickableWidget button);
}
}

View File

@@ -0,0 +1,48 @@
package net.puzzlemc.models.mixin;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.client.render.model.json.ModelElement;
import net.minecraft.util.JsonHelper;
import net.puzzlemc.core.config.PuzzleConfig;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ModelElement.Deserializer.class)
public abstract class MixinModelElementDeserializer {
@Shadow protected abstract Vector3f deserializeVec3f(JsonObject object, String name);
@Inject(at = @At("HEAD"),method = "deserializeRotationAngle", cancellable = true)
private void puzzle$deserializeRotationAngle(JsonObject object, CallbackInfoReturnable<Float> cir) {
if (PuzzleConfig.unlimitedRotations) {
float angle = JsonHelper.getFloat(object, "angle");
cir.setReturnValue(angle);
}
}
@Inject(at = @At("HEAD"),method = "deserializeTo", cancellable = true)
private void puzzle$deserializeTo(JsonObject object, CallbackInfoReturnable<Vector3f> cir) {
if (PuzzleConfig.biggerModels) {
Vector3f vec3f = this.deserializeVec3f(object, "to");
if (!(vec3f.x < -32.0F) && !(vec3f.y < -32.0F) && !(vec3f.z < -32.0F) && !(vec3f.x > 48.0F) && !(vec3f.y > 48.0F) && !(vec3f.z > 48.0F)) {
cir.setReturnValue(vec3f);
} else {
throw new JsonParseException("'to' specifier exceeds the allowed boundaries: " + vec3f);
}
}
}
@Inject(at = @At("HEAD"),method = "deserializeFrom", cancellable = true)
private void puzzle$deserializeFrom(JsonObject object, CallbackInfoReturnable<Vector3f> cir) {
if (PuzzleConfig.biggerModels) {
Vector3f vec3f = this.deserializeVec3f(object, "from");
if (!(vec3f.x < -32.0F) && !(vec3f.y < -32.0F) && !(vec3f.z < -32.0F) && !(vec3f.x > 48.0F) && !(vec3f.y > 48.0F) && !(vec3f.z > 48.0F)) {
cir.setReturnValue(vec3f);
} else {
throw new JsonParseException("'from' specifier exceeds the allowed boundaries: " + vec3f);
}
}
}
}

View File

@@ -0,0 +1,173 @@
package net.puzzlemc.splashscreen;
import eu.midnightdust.lib.util.MidnightColorUtil;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.resource.*;
import net.minecraft.util.Util;
import net.puzzlemc.core.config.PuzzleConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.metadata.TextureResourceMetadata;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.ResourceTexture;
import net.minecraft.util.Identifier;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static net.puzzlemc.core.PuzzleCore.LOGGER;
import static net.puzzlemc.core.PuzzleCore.MOD_ID;
public class PuzzleSplashScreen {
public static final Identifier LOGO = Identifier.of("textures/gui/title/mojangstudios.png");
public static final Identifier BACKGROUND = Identifier.of("puzzle/splash_background.png");
public static File CONFIG_PATH = new File(String.valueOf(PlatformFunctions.getConfigDirectory().resolve(".puzzle_cache")));
public static Path LOGO_TEXTURE = Paths.get(CONFIG_PATH + "/mojangstudios.png");
public static Path BACKGROUND_TEXTURE = Paths.get(CONFIG_PATH + "/splash_background.png");
private static final MinecraftClient client = MinecraftClient.getInstance();
private static boolean keepBackground = false;
public static void init() {
if (!CONFIG_PATH.exists()) { // Run when config directory is nonexistent //
if (CONFIG_PATH.mkdir()) { // Create our custom config directory //
if (Util.getOperatingSystem().equals(Util.OperatingSystem.WINDOWS)) {
try { Files.setAttribute(CONFIG_PATH.toPath(), "dos:hidden", true);
} catch (IOException ignored) {}
}
}
}
}
public static class ReloadListener implements SynchronousResourceReloader {
public static final ReloadListener INSTANCE = new ReloadListener();
private ReloadListener() {}
@Override
public void reload(ResourceManager manager) {
if (PuzzleConfig.resourcepackSplashScreen) {
PuzzleSplashScreen.resetColors();
client.getTextureManager().registerTexture(LOGO, new LogoTexture(LOGO));
client.getTextureManager().registerTexture(BACKGROUND, new LogoTexture(BACKGROUND));
manager.findResources("optifine", path -> path.getPath().contains("color.properties")).forEach((id, resource) -> {
try (InputStream stream = resource.getInputStream()) {
Properties properties = new Properties();
properties.load(stream);
if (properties.get("screen.loading") != null) {
PuzzleConfig.backgroundColor = MidnightColorUtil.hex2Rgb(properties.get("screen.loading").toString()).getRGB();
PuzzleConfig.hasCustomSplashScreen = true;
}
if (properties.get("screen.loading.bar") != null) {
PuzzleConfig.progressBarBackgroundColor = MidnightColorUtil.hex2Rgb(properties.get("screen.loading.bar").toString()).getRGB();
PuzzleConfig.hasCustomSplashScreen = true;
}
if (properties.get("screen.loading.progress") != null) {
PuzzleConfig.progressBarColor = MidnightColorUtil.hex2Rgb(properties.get("screen.loading.progress").toString()).getRGB();
PuzzleConfig.hasCustomSplashScreen = true;
}
if (properties.get("screen.loading.outline") != null) {
PuzzleConfig.progressFrameColor = MidnightColorUtil.hex2Rgb(properties.get("screen.loading.outline").toString()).getRGB();
PuzzleConfig.hasCustomSplashScreen = true;
}
if (properties.get("screen.loading.blend") != null) {
PuzzleConfig.disableBlend = properties.get("screen.loading.blend").toString().equals("off");
PuzzleConfig.customBlendFunction = new ArrayList<>(Arrays.stream(properties.get("screen.loading.blend").toString().split(" ")).toList());
PuzzleConfig.hasCustomSplashScreen = true;
}
} catch (Exception e) {
LOGGER.error("Error occurred while loading color.properties {}", id.toString(), e);
}
});
AtomicInteger logoCount = new AtomicInteger();
manager.findResources("textures", path -> path.getPath().contains("mojangstudios.png")).forEach((id, resource) -> {
try (InputStream stream = resource.getInputStream()) {
Files.copy(stream, LOGO_TEXTURE, StandardCopyOption.REPLACE_EXISTING);
client.getTextureManager().registerTexture(LOGO, new DynamicLogoTexture());
if (logoCount.get() > 0) PuzzleConfig.hasCustomSplashScreen = true;
logoCount.getAndIncrement();
} catch (Exception e) {
LOGGER.error("Error occurred while loading custom minecraft logo {}", id.toString(), e);
}
});
manager.findResources(MOD_ID, path -> path.getPath().contains("splash_background.png")).forEach((id, resource) -> {
try (InputStream stream = resource.getInputStream()) {
Files.copy(stream, BACKGROUND_TEXTURE, StandardCopyOption.REPLACE_EXISTING);
InputStream input = new FileInputStream(String.valueOf(PuzzleSplashScreen.BACKGROUND_TEXTURE));
client.getTextureManager().registerTexture(BACKGROUND, new NativeImageBackedTexture(NativeImage.read(input)));
keepBackground = true;
PuzzleConfig.hasCustomSplashScreen = true;
} catch (Exception e) {
LOGGER.error("Error occurred while loading custom splash background {}", id.toString(), e);
}
});
if (!keepBackground) {
try {
Files.delete(BACKGROUND_TEXTURE);
} catch (Exception ignored) {}
}
keepBackground = false;
PuzzleConfig.write(MOD_ID);
}
}
}
public static void resetColors() {
PuzzleConfig.backgroundColor = 15675965;
PuzzleConfig.progressBarColor = 16777215;
PuzzleConfig.progressBarBackgroundColor = 15675965;
PuzzleConfig.progressFrameColor = 16777215;
PuzzleConfig.disableBlend = false;
PuzzleConfig.customBlendFunction = new ArrayList<>();
PuzzleConfig.hasCustomSplashScreen = false;
}
public static class LogoTexture extends ResourceTexture {
public LogoTexture(Identifier logo) { super(logo); }
protected TextureData loadTextureData(ResourceManager resourceManager) {
MinecraftClient minecraftClient = MinecraftClient.getInstance();
DefaultResourcePack defaultResourcePack = minecraftClient.getDefaultResourcePack();
try {
InputStream inputStream = Objects.requireNonNull(defaultResourcePack.open(ResourceType.CLIENT_RESOURCES, LOGO)).get();
TextureData var6;
try {
var6 = new TextureData(new TextureResourceMetadata(true, true), NativeImage.read(inputStream));
} finally {
if (inputStream != null) {
inputStream.close();
}
}
return var6;
} catch (IOException var18) {
return new TextureData(var18);
}
}
}
public static class DynamicLogoTexture extends ResourceTexture {
public DynamicLogoTexture() {
super(LOGO);
}
protected TextureData loadTextureData(ResourceManager resourceManager) {
try {
InputStream input = new FileInputStream(String.valueOf(PuzzleSplashScreen.LOGO_TEXTURE));
return new TextureData(new TextureResourceMetadata(true, true), NativeImage.read(input));
} catch (IOException e) {
LOGGER.error("Encountered an error during logo loading: ", e);
return TextureData.load(resourceManager, LOGO);
}
}
}
}

View File

@@ -0,0 +1,114 @@
package net.puzzlemc.splashscreen.mixin;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Overlay;
import net.minecraft.client.gui.screen.SplashOverlay;
import net.minecraft.client.render.*;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
import net.puzzlemc.core.config.PuzzleConfig;
import net.puzzlemc.splashscreen.PuzzleSplashScreen;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.function.IntSupplier;
import static net.puzzlemc.splashscreen.PuzzleSplashScreen.BACKGROUND;
@Mixin(value = SplashOverlay.class, priority = 2000)
public abstract class MixinSplashScreen extends Overlay {
@Shadow @Final static Identifier LOGO;
@Shadow private long reloadCompleteTime;
@Shadow @Final private MinecraftClient client;
@Shadow @Final private boolean reloading;
@Shadow private long reloadStartTime;
@Shadow
private static int withAlpha(int color, int alpha) {
return 0;
}
@Inject(method = "init(Lnet/minecraft/client/MinecraftClient;)V", at = @At("TAIL"))
private static void puzzle$initSplashscreen(MinecraftClient client, CallbackInfo ci) { // Load our custom textures at game start //
if (PuzzleConfig.resourcepackSplashScreen) {
if (PuzzleSplashScreen.LOGO_TEXTURE.toFile().exists()) {
client.getTextureManager().registerTexture(LOGO, new PuzzleSplashScreen.DynamicLogoTexture());
}
if (PuzzleSplashScreen.BACKGROUND_TEXTURE.toFile().exists()) {
try {
InputStream input = new FileInputStream(String.valueOf(PuzzleSplashScreen.BACKGROUND_TEXTURE));
client.getTextureManager().registerTexture(BACKGROUND, new NativeImageBackedTexture(NativeImage.read(input)));
} catch (IOException ignored) {}
}
}
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/function/IntSupplier;getAsInt()I"))
private int puzzle$modifyBackground(IntSupplier instance) { // Set the Progress Bar Frame Color to our configured value //
return (!PuzzleConfig.resourcepackSplashScreen || PuzzleConfig.progressBarBackgroundColor == 15675965) ? instance.getAsInt() : PuzzleConfig.backgroundColor | 255 << 24;
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;blendFunc(II)V", shift = At.Shift.AFTER), remap = false)
private void puzzle$betterBlend(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (PuzzleConfig.resourcepackSplashScreen) {
if (PuzzleConfig.disableBlend) RenderSystem.defaultBlendFunc();
else if (PuzzleConfig.customBlendFunction.size() == 4) RenderSystem.blendFuncSeparate(
GlStateManager.SrcFactor.valueOf(PuzzleConfig.customBlendFunction.get(0)),
GlStateManager.DstFactor.valueOf(PuzzleConfig.customBlendFunction.get(1)),
GlStateManager.SrcFactor.valueOf(PuzzleConfig.customBlendFunction.get(2)),
GlStateManager.DstFactor.valueOf(PuzzleConfig.customBlendFunction.get(3)));
}
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;getScaledWindowWidth()I", ordinal = 2))
private void puzzle$renderSplashBackground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (Files.exists(PuzzleSplashScreen.BACKGROUND_TEXTURE) && PuzzleConfig.resourcepackSplashScreen) {
int width = client.getWindow().getScaledWidth();
int height = client.getWindow().getScaledHeight();
long l = Util.getMeasuringTimeMs();
float f = this.reloadCompleteTime > -1L ? (float)(l - this.reloadCompleteTime) / 1000.0F : -1.0F;
float g = this.reloadStartTime> -1L ? (float)(l - this.reloadStartTime) / 500.0F : -1.0F;
float s;
if (f >= 1.0F) s = 1.0F - MathHelper.clamp(f - 1.0F, 0.0F, 1.0F);
else if (reloading) s = MathHelper.clamp(g, 0.0F, 1.0F);
else s = 1.0F;
RenderSystem.enableBlend();
RenderSystem.blendEquation(32774);
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionTexProgram);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, s);
context.drawTexture(BACKGROUND, 0, 0, 1, 0, 0, width, height, width, height);
RenderSystem.defaultBlendFunc();
RenderSystem.disableBlend();
}
}
@Inject(method = "renderProgressBar", at = @At("HEAD"))
private void puzzle$addProgressBarBackground(DrawContext context, int minX, int minY, int maxX, int maxY, float opacity, CallbackInfo ci) {
context.getMatrices().translate(0, 0, 1f);
if (!PuzzleConfig.resourcepackSplashScreen || PuzzleConfig.progressBarBackgroundColor == 15675965) return;
long l = Util.getMeasuringTimeMs();
float f = this.reloadCompleteTime > -1L ? (float)(l - this.reloadCompleteTime) / 1000.0F : -1.0F;
int m = MathHelper.ceil((1.0F - MathHelper.clamp(f - 1.0F, 0.0F, 1.0F)) * 255.0F);
RenderSystem.disableBlend();
context.fill(minX, minY, maxX, maxY, withAlpha(PuzzleConfig.progressBarBackgroundColor, m));
}
@ModifyArg(method = "renderProgressBar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;fill(IIIII)V"), index = 4)
private int puzzle$modifyProgressFrame(int color) { // Set the Progress Bar Frame Color to our configured value //
return (!PuzzleConfig.resourcepackSplashScreen || PuzzleConfig.progressFrameColor == 16777215) ? color : PuzzleConfig.progressFrameColor | 255 << 24;
}
@ModifyArg(method = "renderProgressBar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;fill(IIIII)V", ordinal = 0), index = 4)
private int puzzle$modifyProgressColor(int color) { // Set the Progress Bar Color to our configured value //
return (!PuzzleConfig.resourcepackSplashScreen || PuzzleConfig.progressBarColor == 16777215) ? color : PuzzleConfig.progressBarColor | 255 << 24;
}
}