Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0669277
start chat rules improvements
viciscat Nov 6, 2025
19b829e
layouting
viciscat Nov 14, 2025
b0483ae
redo the layout
viciscat Nov 14, 2025
36cb843
move locations button
viciscat Nov 14, 2025
bd6b112
center name
viciscat Nov 14, 2025
8c5ff0c
colors
viciscat Nov 14, 2025
b3cd7fa
bold
viciscat Nov 14, 2025
2de318e
fix tests
viciscat Nov 14, 2025
34403ed
DFU
viciscat Nov 15, 2025
3b46bc9
rename
viciscat Nov 15, 2025
b3d716d
more accurate to original config
viciscat Nov 15, 2025
05b7e0e
Merge master
Alex33856 Nov 18, 2025
e678312
add command to open custom chat rules screen
viciscat Nov 19, 2025
3755d51
Merge master
Alex33856 Nov 28, 2025
3ad2dfc
spotlessApply
Alex33856 Nov 28, 2025
2d8f9aa
fix disconnect
viciscat Nov 29, 2025
485ed8b
fix toast options showing if you don't have a toast message
viciscat Nov 29, 2025
efe5d99
replaceFirst adds the tail, we don't want that
viciscat Nov 29, 2025
88eeb78
Merge branch 'master' into chat-rules-improvements
viciscat Dec 4, 2025
41effe6
fix merge
viciscat Dec 4, 2025
643ec1d
announcement duration
viciscat Dec 4, 2025
87f13ad
spotlessApply
viciscat Dec 4, 2025
14c1684
fix tests
viciscat Dec 4, 2025
58eb75c
Merge branch 'master' into chat-rules-improvements
viciscat Dec 6, 2025
69cb7ee
remove star imports
viciscat Dec 6, 2025
2cc1da5
some annotations
viciscat Dec 6, 2025
f2529ca
replace asserts with Objects.requireNonNull
viciscat Dec 6, 2025
33ea36a
Merge branch 'master' into chat-rules-improvements
viciscat Dec 12, 2025
4ac38f4
1.21.11
viciscat Dec 12, 2025
12cecfd
prepare mojmap transition
viciscat Dec 13, 2025
a94d26c
Merge branch 'master' into chat-rules-improvements
viciscat Dec 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/main/java/de/hysky/skyblocker/SkyblockerMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import de.hysky.skyblocker.annotations.Init;
import de.hysky.skyblocker.config.ConfigNullFieldsFix;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.config.datafixer.ConfigDataFixer;
import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview;
import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListManager;
import de.hysky.skyblocker.utils.Utils;
Expand Down Expand Up @@ -48,7 +47,6 @@ public static Identifier id(String path) {
@Override
public void onInitializeClient() {
ClientTickEvents.END_CLIENT_TICK.register(this::tick);
ConfigDataFixer.apply();
SkyblockerConfigManager.init();
ConfigNullFieldsFix.init(); //DO NOT INIT ANY CLASS THAT USES CONFIG FIELDS BEFORE THIS!
ConfigBackupManager.init();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.hysky.skyblocker.config;

import com.mojang.brigadier.arguments.StringArgumentType;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.logging.LogUtils;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.backup.ConfigBackupManager;
import de.hysky.skyblocker.config.categories.ChatCategory;
Expand All @@ -20,8 +23,10 @@
import de.hysky.skyblocker.config.categories.QuickNavigationCategory;
import de.hysky.skyblocker.config.categories.SlayersCategory;
import de.hysky.skyblocker.config.categories.UIAndVisualsCategory;
import de.hysky.skyblocker.config.datafixer.ConfigDataFixer;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.mixins.accessors.AbstractContainerScreenAccessor;
import de.hysky.skyblocker.utils.datafixer.JsonHelper;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import net.azureaaron.dandelion.platform.ConfigType;
import net.azureaaron.dandelion.systems.ConfigManager;
Expand All @@ -39,17 +44,23 @@
import org.apache.commons.lang3.function.Consumers;
import org.jspecify.annotations.Nullable;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.lang.StackWalker.Option;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;

import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
import org.slf4j.Logger;

public class SkyblockerConfigManager {
public static final int CONFIG_VERSION = 4;
private static final Path CONFIG_FILE = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json");
public static final int CONFIG_VERSION = 6;
private static final Logger LOGGER = LogUtils.getLogger();
private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir();
private static final Path CONFIG_FILE = CONFIG_DIR.resolve("skyblocker.json");
private static final ConfigManager<SkyblockerConfig> CONFIG_MANAGER = ConfigManager.create(SkyblockerConfig.class, CONFIG_FILE, UnaryOperator.identity());

public static SkyblockerConfig get() {
Expand All @@ -64,6 +75,7 @@ public static void init() {
if (StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass() != SkyblockerMod.class) {
throw new RuntimeException("Skyblocker: Called config init from an illegal place!");
}
dataFix(CONFIG_FILE, CONFIG_DIR.resolve("skyblocker.json.old"));

CONFIG_MANAGER.load();
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(configLiteral("config")).then(configLiteral("options"))));
Expand Down Expand Up @@ -142,4 +154,43 @@ private static LiteralArgumentBuilder<FabricClientCommandSource> configLiteral(S
return literal(name).executes(Scheduler.queueOpenScreenCommand(() -> createGUI(null)))
.then(argument("option", StringArgumentType.greedyString()).executes((ctx) -> Scheduler.queueOpenScreen(createGUI(null, ctx.getArgument("option", String.class)))));
}

public static void dataFix(Path configDir, Path backupDir) {
//User is new - has no config file (or maybe config folder)
if (!Files.exists(CONFIG_DIR) || !Files.exists(configDir)) return;

//Should never be null if the file exists unless its malformed JSON or something in which case well it gets reset
JsonObject oldConfig = loadConfig(configDir);
if (oldConfig == null || JsonHelper.getInt(oldConfig, "version").orElse(1) == CONFIG_VERSION) return;

JsonObject newConfig = ConfigDataFixer.apply(ConfigDataFixer.CONFIG_TYPE, oldConfig);

//Write the updated file
if (!writeConfig(configDir, newConfig)) {
LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to fix up config file!");
writeConfig(backupDir, oldConfig);
}
}

private static @Nullable JsonObject loadConfig(Path path) {
try (BufferedReader reader = Files.newBufferedReader(path)) {
return JsonParser.parseReader(reader).getAsJsonObject();
} catch (Throwable t) {
LOGGER.error("[Skyblocker Config Data Fixer] Failed to load config file!", t);
}

return null;
}

private static boolean writeConfig(Path path, JsonObject config) {
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
SkyblockerMod.GSON.toJson(config, writer);

return true;
} catch (Throwable t) {
LOGGER.error("[Skyblocker Config Data Fixer] Failed to save config file at {}!", path, t);
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.configs.QuickNavigationConfig;
import de.hysky.skyblocker.config.screens.quicknav.ItemSelectionScreen;
import de.hysky.skyblocker.utils.render.gui.ItemSelectionPopup;
import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer;
import net.azureaaron.dandelion.systems.ButtonOption;
import net.azureaaron.dandelion.systems.ConfigCategory;
import net.azureaaron.dandelion.systems.Option;
Expand Down Expand Up @@ -71,7 +72,11 @@ private static OptionGroup quickNavButton(QuickNavigationConfig.QuickNavItem def
.option(ButtonOption.createBuilder()
.name(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem"))
.description(Component.translatable("skyblocker.config.quickNav.button.chooseSkyblockItem.@Tooltip"))
.action(screen -> Minecraft.getInstance().setScreen(new ItemSelectionScreen(screen, button.itemData)))
.action(screen -> Minecraft.getInstance().setScreen(new ItemSelectionPopup(screen, item -> {
if (item == null) return;
button.itemData.item = item.getItem();
button.itemData.components = ItemStackComponentizationFixer.componentsAsString(item);
})))
.prompt(Component.translatable("text.skyblocker.open"))
.build())
.option(Option.<Item>createBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ public ConfigDataFix(Schema outputSchema, boolean changesType) {
}

protected <T> Dynamic<T> fixVersion(Dynamic<T> dynamic) {
return dynamic.set("version", dynamic.createInt(DataFixUtils.getVersion(getVersionKey())));
return dynamic.set(ConfigDataFixer.VERSION_KEY, dynamic.createInt(DataFixUtils.getVersion(getVersionKey())));
}
}
Original file line number Diff line number Diff line change
@@ -1,66 +1,52 @@
package de.hysky.skyblocker.config.datafixer;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.DataFixerBuilder;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.datafixer.JsonHelper;
import net.fabricmc.loader.api.FabricLoader;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Path;

public class ConfigDataFixer {
static final String VERSION_KEY = "version";
protected static final Logger LOGGER = LogUtils.getLogger();
private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir();
public static final DSL.TypeReference CONFIG_TYPE = () -> "config";
public static final DSL.TypeReference CHAT_RULES_TYPE = () -> "chat_rules";
private static @Nullable DataFixer dataFixer;

public static void apply() {
apply(CONFIG_DIR.resolve("skyblocker.json"), CONFIG_DIR.resolve("skyblocker.json.old"));
public static JsonObject apply(DSL.TypeReference type, JsonObject oldConfig) {
return apply(type, oldConfig, SkyblockerConfigManager.CONFIG_VERSION);
}

public static void apply(Path configDir, Path backupDir) {
//User is new - has no config file (or maybe config folder)
if (!Files.exists(CONFIG_DIR) || !Files.exists(configDir)) return;

//Should never be null if the file exists unless its malformed JSON or something in which case well it gets reset
JsonObject oldConfig = loadConfig(configDir);
if (oldConfig == null || JsonHelper.getInt(oldConfig, "version").orElse(1) == SkyblockerConfigManager.CONFIG_VERSION) return;

JsonObject newConfig = apply(oldConfig);

//Write the updated file
if (!writeConfig(configDir, newConfig)) {
LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to fix up config file!");
writeConfig(backupDir, oldConfig);
}
public static <T> Dynamic<T> apply(DSL.TypeReference type, Dynamic<T> oldConfig) {
return apply(type, oldConfig, SkyblockerConfigManager.CONFIG_VERSION);
}

public static JsonObject apply(JsonObject oldConfig) {
return apply(oldConfig, SkyblockerConfigManager.CONFIG_VERSION);
}

public static JsonObject apply(JsonObject oldConfig, int newVersion) {
public static <T> Dynamic<T> apply(DSL.TypeReference type, Dynamic<T> oldConfig, int newVersion) {
long start = System.currentTimeMillis();

JsonObject newConfig = build().update(CONFIG_TYPE, new Dynamic<>(JsonOps.INSTANCE, oldConfig), JsonHelper.getInt(oldConfig, "version").orElse(1), newVersion).getValue().getAsJsonObject();
Dynamic<T> newConfig = build().update(type, oldConfig, oldConfig.get(VERSION_KEY).asInt(1), newVersion);

long end = System.currentTimeMillis();
LOGGER.info("[Skyblocker Config Data Fixer] Applied datafixers in {} ms!", end - start);
LOGGER.info("[Skyblocker Config Data Fixer] Applied {} datafixers in {} ms!", type.typeName(), end - start);
return newConfig;
}

public static JsonObject apply(DSL.TypeReference type, JsonObject oldConfig, int newVersion) {
return apply(type, new Dynamic<>(JsonOps.INSTANCE, oldConfig), newVersion).getValue().getAsJsonObject();
}

private static DataFixer build() {
if (dataFixer != null) return dataFixer;
DataFixerBuilder builder = new DataFixerBuilder(SkyblockerConfigManager.CONFIG_VERSION);

builder.addSchema(1, ConfigSchema::new);
Expand All @@ -70,29 +56,26 @@ private static DataFixer build() {
builder.addFixer(new ConfigFix2QuickNav(schema3, true));
Schema schema4 = builder.addSchema(4, Schema::new);
builder.addFixer(new ConfigFix3AnimatedDyeAndItemBackground(schema4, true));
Schema schema5 = builder.addSchema(5, Schema::new);
builder.addFixer(new ConfigFix4ChatRulesObject(schema5, true));
Schema schema6 = builder.addSchema(6, Schema::new);
builder.addFixer(new ConfigFix5ChatRulesSeparateOutputs(schema6, true));

return builder.build().fixer();
}

private static JsonObject loadConfig(Path path) {
try (BufferedReader reader = Files.newBufferedReader(path)) {
return JsonParser.parseReader(reader).getAsJsonObject();
} catch (Throwable t) {
LOGGER.error("[Skyblocker Config Data Fixer] Failed to load config file!", t);
}

return null;
return dataFixer = builder.build().fixer();
}

private static boolean writeConfig(Path path, JsonObject config) {
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
SkyblockerMod.GSON.toJson(config, writer);

return true;
} catch (Throwable t) {
LOGGER.error("[Skyblocker Config Data Fixer] Failed to save config file at {}!", path, t);
}

return false;
public static <A> Codec<A> createDataFixingCodec(DSL.TypeReference type, Codec<A> baseCodec) {
return new Codec<>() {
@Override
public <T> DataResult<T> encode(A input, DynamicOps<T> ops, T prefix) {
return baseCodec.encode(input, ops, prefix);
}

@Override
public <T> DataResult<Pair<A, T>> decode(DynamicOps<T> ops, T input) {
Dynamic<T> dynamic2 = apply(type, new Dynamic<>(ops, input));
return baseCodec.decode(dynamic2);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.hysky.skyblocker.config.datafixer;

import com.mojang.datafixers.DSL;
import com.mojang.datafixers.TypeRewriteRule;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;

/**
* Transform the chat rules file from a json list to a json object containing a list.
*/
public class ConfigFix4ChatRulesObject extends ConfigDataFix {
public ConfigFix4ChatRulesObject(Schema outputSchema, boolean changesType) {
super(outputSchema, changesType);
}

@Override
protected TypeRewriteRule makeRule() {
return fixTypeEverywhereTyped(
getClass().getSimpleName(),
getInputSchema().getType(ConfigDataFixer.CHAT_RULES_TYPE),
typed -> typed.update(DSL.remainderFinder(), this::fix)
);
}

private <T> Dynamic<T> fix(Dynamic<T> dynamic) {
if (dynamic.asStreamOpt().result().isPresent()) {
dynamic = dynamic.emptyMap().set("rules", dynamic);
}
return fixVersion(dynamic);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package de.hysky.skyblocker.config.datafixer;

import com.mojang.datafixers.DSL;
import com.mojang.datafixers.TypeRewriteRule;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;
import de.hysky.skyblocker.config.SkyblockerConfigManager;

import java.util.Map;
import java.util.Optional;

/**
* Separates all outputs and renames a few things.
*/
public class ConfigFix5ChatRulesSeparateOutputs extends ConfigDataFix {
public ConfigFix5ChatRulesSeparateOutputs(Schema outputSchema, boolean changesType) {
super(outputSchema, changesType);
}

@Override
protected TypeRewriteRule makeRule() {
return fixTypeEverywhereTyped(
getClass().getSimpleName(),
getInputSchema().getType(ConfigDataFixer.CHAT_RULES_TYPE),
typed -> typed.update(DSL.remainderFinder(), this::fix)
);
}

private <T> Dynamic<T> fix(Dynamic<T> dynamic) {
return fixVersion(dynamic).update("rules", rules -> rules.createList(rules.asStream().map(rule -> rule
.renameField("isPartialMatch", "partialMatch")
.renameField("isRegex", "regex")
.renameField("isIgnoreCase", "ignoreCase")
.renameField("validLocations", "locations")
.renameField("hideMessage", "hideOriginalMessage")
.renameField("replaceMessage", "chatMessage")
.setFieldIfPresent("actionBarMessage", rule.get("showActionBar").result().flatMap(dynamic1 ->
dynamic1.asBoolean(false) ? rule.get("replaceMessage").result() : Optional.empty()
))
.remove("showActionBar")
.setFieldIfPresent("announcementMessage", rule.get("showAnnouncement").result().flatMap(dynamic1 ->
dynamic1.asBoolean(false) ? rule.get("replaceMessage").result().map(message -> dynamic1.createMap(
Map.of(dynamic1.createString("message"), message, dynamic1.createString("displayDuration"), dynamic1.createLong(SkyblockerConfigManager.get().chat.chatRuleConfig.announcementLength * 50L))
)) : Optional.empty()
))
.remove("showAnnouncement")
)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public ConfigSchema(int versionKey, Schema parent) {
@Override
public void registerTypes(Schema schema, Map<String, Supplier<TypeTemplate>> entityTypes, Map<String, Supplier<TypeTemplate>> blockEntityTypes) {
schema.registerType(true, ConfigDataFixer.CONFIG_TYPE, DSL::remainder);
schema.registerType(true, ConfigDataFixer.CHAT_RULES_TYPE, DSL::remainder);
}

@Override
Expand Down
Loading