diff --git a/build.gradle b/build.gradle index e86a716..09661ae 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ archivesBaseName = project.slug repositories { maven { url "https://api.modrinth.com/maven" } maven { url "https://maven.enginehub.org/repo/" } + maven { url "https://maven.gegy.dev/releases/" } mavenCentral() } @@ -33,6 +34,8 @@ dependencies { modImplementation libs.worldeditCore modImplementation libs.worldeditFabric + modImplementation "dev.gegy:player-roles-api:1.6.11" + shade(libs.nettyIoUringClasses) { transitive = false } shade(variantOf(libs.nettyIoUringNative) { classifier "linux-x86_64" }) { transitive = false } shade(libs.zstd) { transitive = false } diff --git a/gradle.properties b/gradle.properties index 687b750..10879ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ authors=jaskarth, unascribed contributors=Patbox, IThundxr license=AGPL-3.0-or-later # Mod Version -baseVersion=0.5.5 +baseVersion=0.6.0 # Branch Metadata branch=1.21 tagBranch=1.21 diff --git a/src/main/java/net/modfest/fireblanket/Fireblanket.java b/src/main/java/net/modfest/fireblanket/Fireblanket.java index 1c4290e..221d639 100644 --- a/src/main/java/net/modfest/fireblanket/Fireblanket.java +++ b/src/main/java/net/modfest/fireblanket/Fireblanket.java @@ -32,6 +32,9 @@ import net.modfest.fireblanket.command.DumpCommand; import net.modfest.fireblanket.command.RegionCommand; import net.modfest.fireblanket.compat.PolyMcCompat; +import net.modfest.fireblanket.compat.roles.PlayerRolesCompat; +import net.modfest.fireblanket.config.ConfigSpecs; +import net.modfest.fireblanket.config.FireblanketConfig; import net.modfest.fireblanket.mixin.accessor.ClientConnectionAccessor; import net.modfest.fireblanket.mixin.accessor.ServerChunkManagerAccessor; import net.modfest.fireblanket.mixin.accessor.ServerLoginNetworkHandlerAccessor; @@ -39,13 +42,14 @@ import net.modfest.fireblanket.net.BatchedBEUpdatePayload; import net.modfest.fireblanket.net.CommandBlockPacket; import net.modfest.fireblanket.world.blocks.UpdateSignBlockEntityTypes; -import net.modfest.fireblanket.world.entity.EntityFilters; import net.modfest.fireblanket.world.render_regions.RegionSyncRequest; import net.modfest.fireblanket.world.render_regions.RenderRegions; import net.modfest.fireblanket.world.render_regions.RenderRegionsState; +import net.modfest.fireblanket.config.EntityFilters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.LinkedBlockingQueue; @@ -90,14 +94,9 @@ public void onInitialize() { UpdateSignBlockEntityTypes.apply(block); }); - Path configs = FabricLoader.getInstance().getConfigDir().resolve("fireblanket"); - if (Files.exists(configs)) { - Path types = configs.resolve("entityfilters.txt"); - if (Files.exists(types)) { - EntityFilters.parse(types); - } - } + + EntityFilters.init(); for (int i = 0; i < PACKET_QUEUES.length; i++) { LinkedBlockingQueue q = new LinkedBlockingQueue<>(); @@ -150,10 +149,12 @@ public void onInitialize() { PolyMcCompat.init(); } + PlayerRolesCompat.init(); + ServerWorldEvents.LOAD.register((server, world) -> { - if (System.getProperty("fireblanket.loadRadius") != null) { + if (FireblanketConfig.get(ConfigSpecs.FORCED_LOAD_RADIUS) > 0) { if (!world.getRegistryKey().getValue().toString().equals("minecraft:overworld")) return; - int radius = Integer.getInteger("fireblanket.loadRadius"); + int radius = FireblanketConfig.get(ConfigSpecs.FORCED_LOAD_RADIUS); int min = (int) Math.floor(-radius / 16); int max = (int) Math.ceil(radius / 16); int count = (max - min) * (max - min); diff --git a/src/main/java/net/modfest/fireblanket/FireblanketMixin.java b/src/main/java/net/modfest/fireblanket/FireblanketMixin.java index 2bcb250..fe3ab53 100644 --- a/src/main/java/net/modfest/fireblanket/FireblanketMixin.java +++ b/src/main/java/net/modfest/fireblanket/FireblanketMixin.java @@ -2,87 +2,38 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.Bootstrap; +import net.modfest.fireblanket.config.ConfigSpecs; +import net.modfest.fireblanket.config.FireblanketConfig; import org.objectweb.asm.tree.ClassNode; import org.slf4j.LoggerFactory; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Set; public class FireblanketMixin implements IMixinConfigPlugin { - public static final boolean GAMEPLAY_CHANGES = Boolean.getBoolean("fireblanket.gameplayChanges"); public static final boolean DO_MASKING = Boolean.getBoolean("fireblanket.masking"); - private static final boolean DO_CHUNK_CACHE = System.getProperty("fireblanket.loadRadius") != null; - public static final boolean ALLOW_LAMBDAMAP_SAVING = Boolean.getBoolean("fireblanket.allowLambdaMapSaving"); - private static final boolean DO_CHUNKSECTION_OPTO = Boolean.getBoolean("fireblanket.flattenChunkPalettes"); - public static final boolean ALLOW_FOOTGUNS = Boolean.getBoolean("fireblanket.allowFootguns"); - public static final boolean AVOID_ZTSD = Boolean.getBoolean("fireblanket.saveAsDat"); @Override public void onLoad(String mixinPackage) { - // aaaaagh - boolean ignoreRenderingMods = Boolean.getBoolean("fireblanket.iSolemnlySwearIWillNotReportRenderingCrashesAndAcceptResponsibilityForBreakage"); - if (ignoreRenderingMods) { - if (FabricLoader.getInstance().isModLoaded("nvidium")) { - LoggerFactory.getLogger("Fireblanket").error("==================================================="); - LoggerFactory.getLogger("Fireblanket").error(" IGNORING THE PRESENCE OF NVIDIUM. "); - LoggerFactory.getLogger("Fireblanket").error("YOU ACCEPT EVERYTHING THAT WILL HAPPEN FROM NOW ON."); - LoggerFactory.getLogger("Fireblanket").error("==================================================="); - } - if (FabricLoader.getInstance().isModLoaded("bobby")) { - LoggerFactory.getLogger("Fireblanket").error("Ignoring the presence of Bobby. Be ready for missing chunks."); + Path configs = FabricLoader.getInstance().getConfigDir().resolve("fireblanket"); + if (!Files.exists(configs)) { + try { + Files.createDirectory(configs); + } catch (IOException e) { + Fireblanket.LOGGER.error("Exception creating fireblanket directory!", e); } } - if (!ignoreRenderingMods && FabricLoader.getInstance().isModLoaded("nvidium")) { - Bootstrap.SYSOUT.println(""" - ---------------------------------------------------------------------------------------- - ###### Fireblanket is cowardly refusing to launch the game with Nvidium installed ###### - ---------------------------------------------------------------------------------------- - Due to the amount of mods that change how rendering works, Nvidium can experience - random crashes and other hard to debug issues. From testing in the build server, Nvidium - doesn't have a lot of impact on the types of fps lag that the pack causes. As such, - it is not recommended to use Nvidium with Blanketcon for stability reasons. - ======================================================================================== - If you would like to ignore these warnings and launch anyway, you must add the following - to your JVM arguments: - -Dfireblanket.iSolemnlySwearIWillNotReportRenderingCrashesAndAcceptResponsibilityForBreakage=true - The JVM will now exit. - """); - System.exit(0xDEAD); - } - if (FabricLoader.getInstance().isModLoaded("entityculling")) { - Bootstrap.SYSOUT.println(""" - ---------------------------------------------------------------------------------------- - ### Fireblanket is cowardly refusing to launch the game with EntityCulling installed ### - ---------------------------------------------------------------------------------------- - EntityCulling causes performance issues and hard-to-debug crashes due to poor use of - threading. Sodium's entity culling is already enabled to optimize this behavior, and - Fireblanket contains additional fixes and optimizations for entities that are tuned - specifically for Blanketcon. - ======================================================================================== - You may not override this. The JVM will now exit. - """); - System.exit(0xDEAD); - } - if (!ignoreRenderingMods && FabricLoader.getInstance().isModLoaded("bobby")) { - Bootstrap.SYSOUT.println(""" - ---------------------------------------------------------------------------------------- - ####### Fireblanket is cowardly refusing to launch the game with Bobby installed ####### - ---------------------------------------------------------------------------------------- - While Bobby can make navigating the map easier, it is causing difficult to debug - issues with chunk culling that keep resulting in false issue reports. We do not ship it - with the pack for a reason — we have had so many other issues to chase and fix that we - simply do not have time to field the Bobby issues. It additionally can cause crashes if - its option to keep block entities in fake chunks is not enabled, so it is not - recommended for use due to stability reasons. - ======================================================================================== - If you would like to ignore these warnings and launch anyway, you must add the following - to your JVM arguments: - -Dfireblanket.iSolemnlySwearIWillNotReportRenderingCrashesAndAcceptResponsibilityForBreakage=true - The JVM will now exit. - """); - System.exit(0xDEAD); + + FireblanketConfig.init(); + + boolean ignoreRenderingMods = Boolean.getBoolean("fireblanket.iSolemnlySwearIWillNotReportRenderingCrashesAndAcceptResponsibilityForBreakage"); + if (ignoreRenderingMods) { + LoggerFactory.getLogger("Fireblanket").error("Ignoring the presence of rendering mods. You are proceeding at your own mortal peril."); } } @@ -98,27 +49,23 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { } if (mixinClassName.contains("region_chunk_cache")) { - return DO_CHUNK_CACHE; - } - - if (mixinClassName.contains("lambdamap")) { - return !ALLOW_LAMBDAMAP_SAVING; + return FireblanketConfig.get(ConfigSpecs.FORCED_LOAD_RADIUS) > 0; } if (mixinClassName.contains("block_format")) { - return DO_CHUNKSECTION_OPTO; + return FireblanketConfig.get(ConfigSpecs.FLATTEN_CHUNK_PALETTES); } if (mixinClassName.contains("ai") || mixinClassName.contains("sounds")) { - return GAMEPLAY_CHANGES; + return FireblanketConfig.get(ConfigSpecs.GAMEPLAY_CHANGES); } if (mixinClassName.contains("footgun")) { - return !ALLOW_FOOTGUNS; + return !FireblanketConfig.get(ConfigSpecs.ALLOW_FOOTGUNS); } if (mixinClassName.contains("MixinRegionFile") || mixinClassName.contains("MixinPersistentState")) { - return !AVOID_ZTSD; + return !FireblanketConfig.get(ConfigSpecs.AVOID_ZSTD); } // Conflicts with Krypton, which also lifts the limit diff --git a/src/main/java/net/modfest/fireblanket/command/CmdFindReplaceCommand.java b/src/main/java/net/modfest/fireblanket/command/CmdFindReplaceCommand.java index 68b995d..31cf06d 100644 --- a/src/main/java/net/modfest/fireblanket/command/CmdFindReplaceCommand.java +++ b/src/main/java/net/modfest/fireblanket/command/CmdFindReplaceCommand.java @@ -18,11 +18,14 @@ import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.modfest.fireblanket.compat.roles.Roles; public class CmdFindReplaceCommand { public static void init(LiteralArgumentBuilder base, CommandRegistryAccess access) { base.then(literal("cmd-find-replace") - .requires(source -> source.hasPermissionLevel(4)) + .requires(source -> source.hasPermissionLevel(4) && Roles.isNetadmin(source.getPlayer())) .then(argument("regex", StringArgumentType.string()) .then(argument("replacement", StringArgumentType.string()) .executes(ctx -> { diff --git a/src/main/java/net/modfest/fireblanket/command/DumpCommand.java b/src/main/java/net/modfest/fireblanket/command/DumpCommand.java index e287cbc..a65fe63 100644 --- a/src/main/java/net/modfest/fireblanket/command/DumpCommand.java +++ b/src/main/java/net/modfest/fireblanket/command/DumpCommand.java @@ -24,11 +24,13 @@ import java.util.Map; import static net.minecraft.server.command.CommandManager.literal; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.modfest.fireblanket.compat.roles.Roles; public class DumpCommand { public static void init(LiteralArgumentBuilder base, CommandRegistryAccess access) { base.then(literal("dump") - .requires(source -> source.hasPermissionLevel(4)) + .requires(source -> source.hasPermissionLevel(4) || Roles.isOrganizer(source.getPlayer())) .then(literal("command-blocks") .executes(server -> { server.getSource().getServer().submit(() -> { diff --git a/src/main/java/net/modfest/fireblanket/compat/roles/PlayerRolesCompat.java b/src/main/java/net/modfest/fireblanket/compat/roles/PlayerRolesCompat.java new file mode 100644 index 0000000..e51f644 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/compat/roles/PlayerRolesCompat.java @@ -0,0 +1,31 @@ +package net.modfest.fireblanket.compat.roles; + +import dev.gegy.roles.api.PlayerRolesApi; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.entity.player.PlayerEntity; + +public class PlayerRolesCompat { + public static boolean isLoaded = false; + + public static void init() { + if (FabricLoader.getInstance().isModLoaded("player_roles")) { + isLoaded = true; + } + } + + public static boolean isNetadmin(PlayerEntity player) { + return is(player, "netadmin"); + } + + public static boolean isOrganizer(PlayerEntity player) { + return is(player, "organizer"); + } + + public static boolean isBuilder(PlayerEntity player) { + return is(player, "builder") || is(player, "fixer"); + } + + private static boolean is(PlayerEntity player, String id) { + return PlayerRolesApi.lookup().byPlayer(player).stream().anyMatch(r -> r.getId().equals(id)); + } +} diff --git a/src/main/java/net/modfest/fireblanket/compat/roles/Roles.java b/src/main/java/net/modfest/fireblanket/compat/roles/Roles.java new file mode 100644 index 0000000..aaaf07c --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/compat/roles/Roles.java @@ -0,0 +1,75 @@ +package net.modfest.fireblanket.compat.roles; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.integrated.IntegratedServer; + +public class Roles { + private static boolean isSingleplayer(PlayerEntity player) { + return player.getEntityWorld().getServer() instanceof IntegratedServer; + } + + public static boolean isNetadmin(PlayerEntity player) { + if (player == null) { + return false; + } + + if (PlayerRolesCompat.isLoaded) { + if (PlayerRolesCompat.isNetadmin(player)) { + return true; + } + } + + if (isSingleplayer(player)) { + return true; + } + + // check fb config as well + return false; + } + + public static boolean isOrganizer(PlayerEntity player) { + if (player == null) { + return false; + } + + if (isNetadmin(player)) { + return true; + } + + if (PlayerRolesCompat.isLoaded) { + if (PlayerRolesCompat.isOrganizer(player)) { + return true; + } + } + + if (isSingleplayer(player)) { + return true; + } + + // check fb config as well + return false; + } + + public static boolean isBuilder(PlayerEntity player) { + if (player == null) { + return false; + } + + if (isOrganizer(player)) { + return true; + } + + if (PlayerRolesCompat.isLoaded) { + if (PlayerRolesCompat.isBuilder(player)) { + return true; + } + } + + if (isSingleplayer(player)) { + return true; + } + + // check fb config as well + return false; + } +} diff --git a/src/main/java/net/modfest/fireblanket/config/ConfigParsers.java b/src/main/java/net/modfest/fireblanket/config/ConfigParsers.java new file mode 100644 index 0000000..ddfd457 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/ConfigParsers.java @@ -0,0 +1,33 @@ +package net.modfest.fireblanket.config; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ConfigParsers { + public static final ParseFunc> STRING_LIST = in -> { + return Arrays.asList(in.split(",")); + }; + + public static final ParseFunc STRING = in -> in; + + public static final ParseFunc INTEGER = in -> { + try { + return Integer.parseInt(in); + } catch (Exception e) { + throw new ParseException("Unknown integer value: " + in); + } + }; + + public static final ParseFunc BOOLEAN = in -> { + if ("yes".equals(in) || "true".equals(in)) { + return true; + } + + if ("no".equals(in) || "false".equals(in)) { + return false; + } + + throw new ParseException("Unknown boolean value: " + in); + }; +} diff --git a/src/main/java/net/modfest/fireblanket/config/ConfigSpec.java b/src/main/java/net/modfest/fireblanket/config/ConfigSpec.java new file mode 100644 index 0000000..eb68074 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/ConfigSpec.java @@ -0,0 +1,4 @@ +package net.modfest.fireblanket.config; + +public record ConfigSpec(String name, String prettyName, String description, String defaultValue, ParseFunc parser) { +} diff --git a/src/main/java/net/modfest/fireblanket/config/ConfigSpecs.java b/src/main/java/net/modfest/fireblanket/config/ConfigSpecs.java new file mode 100644 index 0000000..4968e05 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/ConfigSpecs.java @@ -0,0 +1,75 @@ +package net.modfest.fireblanket.config; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class ConfigSpecs { + public static final ConfigSpec> PRIVILEGED_USERS = new ConfigSpec<>("privileged-users", "Privileged users", + """ + Specifies users that are allowed to perform potentially extremely destructive commands. Treat with EXTREME CAUTION. + Reccomended to match users given the "netadmin" role. + """, "jaskarth,unascribed", ConfigParsers.STRING_LIST); + + public static final ConfigSpec> MIXIN_SCRAM = new ConfigSpec<>("mixin-scram", "Mixin SCRAM", + """ + Force disables mixins from being loaded. Treat with EXTREME CAUTION, as certain mixins are required for functionality. + The format is a regex, so it is possible to disable an entire package. + Example: + mixin-scram: ai.MixinTemptGoal,client.hooks.*; + """, "", ConfigParsers.STRING_LIST); + + public static final ConfigSpec FORCED_LOAD_RADIUS = new ConfigSpec<>("forced-load-radius", "Forced load radius", + """ + Chunk accesses and loading can become very expensive for a large fest. This option keeps a predefined region of chunks + loaded (but not ticking), so that cold accesses don't suffer a fetch from disk. WARNING: This option can dramatically + increase the memory usage of the server, so treat with care! A value of '0' disables this optimization. + Value is defined in blocks. + """, "0", ConfigParsers.INTEGER); + + public static final ConfigSpec FLATTEN_CHUNK_PALETTES = new ConfigSpec<>("flatten-chunk-palettes", "Flatten chunk palettes", + """ + Flatten chunk data to use an array instead of looking up a palette each time. This allows significantly faster lookups + at the cost of tremendous memory usage. + """, "no", ConfigParsers.BOOLEAN); + + public static final ConfigSpec ALLOW_FOOTGUNS = new ConfigSpec<>("allow-footguns", "Allow Footguns", + """ + Disable pre-emptive disabling of @e selectors unless 'force=true' is also specified. + """, "no", ConfigParsers.BOOLEAN); + + public static final ConfigSpec AVOID_ZSTD = new ConfigSpec<>("avoid-zstd", "Avoid zstd", + """ + Disables the usage of zstd to save world data and compress network connections. + """, "no", ConfigParsers.BOOLEAN); + + public static final ConfigSpec GAMEPLAY_CHANGES = new ConfigSpec<>("gameplay-changes", "Gameplay Changes", + """ + Allow altering features in a way that impacts gameplay. + """, "no", ConfigParsers.BOOLEAN); + + + public static final List> ALL_SPECS = new ArrayList<>(); + + public static final Map> BY_NAME = new HashMap<>(); + + private static void buildMap() { + for (ConfigSpec spec : ALL_SPECS) { + BY_NAME.put(spec.name(), spec); + } + } + + static { + ALL_SPECS.add(PRIVILEGED_USERS); + ALL_SPECS.add(MIXIN_SCRAM); + ALL_SPECS.add(FORCED_LOAD_RADIUS); + ALL_SPECS.add(FLATTEN_CHUNK_PALETTES); + ALL_SPECS.add(ALLOW_FOOTGUNS); + ALL_SPECS.add(AVOID_ZSTD); + ALL_SPECS.add(GAMEPLAY_CHANGES); + + // We have registries at home: + buildMap(); + } +} diff --git a/src/main/java/net/modfest/fireblanket/world/entity/EntityFilters.java b/src/main/java/net/modfest/fireblanket/config/EntityFilters.java similarity index 67% rename from src/main/java/net/modfest/fireblanket/world/entity/EntityFilters.java rename to src/main/java/net/modfest/fireblanket/config/EntityFilters.java index 7c79400..e3d3d9c 100644 --- a/src/main/java/net/modfest/fireblanket/world/entity/EntityFilters.java +++ b/src/main/java/net/modfest/fireblanket/config/EntityFilters.java @@ -1,13 +1,16 @@ -package net.modfest.fireblanket.world.entity; +package net.modfest.fireblanket.config; -import com.google.common.io.Files; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.EntityType; import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; import net.modfest.fireblanket.Fireblanket; import net.modfest.fireblanket.mixin.accessor.EntityTypeAccessor; +import net.modfest.fireblanket.world.entity.EntityFilter; +import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashSet; @@ -23,7 +26,7 @@ public static void parse(Path path) { List strings = new ArrayList<>(); try { - List read = Files.readLines(path.toFile(), Charset.defaultCharset()); + List read = Files.readAllLines(path, Charset.defaultCharset()); strings.addAll(read); } catch (Exception e) { Fireblanket.LOGGER.error("Exception parsing entity filters", e); @@ -53,6 +56,32 @@ public static void parse(Path path) { } } + public static void writeDefault(Path path) { + try { + Files.writeString(path, """ + # Pattern, Tracking range (in chunks), update rate (ticks), Force alwaysUpdateVelocity off [optional] + # Octothorpes on newlines are comments, each definition is delimited by newlines + # Example: + # + # Don't tick item displays and text displays as often. + # minecraft:\\w+_display 5 8 true + """); + } catch (IOException e) { + Fireblanket.LOGGER.error("Exception writing initial entity filters", e); + } + } + + public static void init() { + Path configs = FabricLoader.getInstance().getConfigDir().resolve("fireblanket"); + Path types = configs.resolve("entityfilters.txt"); + + if (Files.exists(types)) { + parse(types); + } else { + writeDefault(types); + } + } + public static void apply() { for (EntityFilter filter : FILTERS) { Fireblanket.LOGGER.debug("Applying entity type filter " + filter.pattern().pattern()); diff --git a/src/main/java/net/modfest/fireblanket/config/FireblanketConfig.java b/src/main/java/net/modfest/fireblanket/config/FireblanketConfig.java new file mode 100644 index 0000000..a142570 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/FireblanketConfig.java @@ -0,0 +1,93 @@ +package net.modfest.fireblanket.config; + +import net.fabricmc.loader.api.FabricLoader; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FireblanketConfig { + private static Map, Object> map = new HashMap<>(); + + public static T get(ConfigSpec spec) { + return (T) map.computeIfAbsent(spec, k -> spec.parser().parse(spec.defaultValue())); + } + + private static void put(ConfigSpec spec, Object value) { + map.put(spec, value); + } + + public static void init() { + Path configs = FabricLoader.getInstance().getConfigDir().resolve("fireblanket"); + Path file = configs.resolve("fireblanket.txt"); + + try { + if (Files.exists(file)) { + parse(Files.readAllLines(file)); + } else { + writeDefault(file); + } + } catch (IOException e) { + throw new RuntimeException("Unable to parse fireblanket.txt", e); + } + } + + private static void writeDefault(Path path) { + List strings = new ArrayList<>(); + strings.add("### Fireblanket main config"); + strings.add("# Octothorpes are comments and can only be found at the start of a line."); + + for (ConfigSpec spec : ConfigSpecs.ALL_SPECS) { + strings.add(""); + + strings.add("## " + spec.prettyName()); + + for (String str : spec.description().split("\n")) { + strings.add("# " + str); + } + + String v = spec.name() + ": " + spec.defaultValue() + ";"; + strings.add(v); + } + + try { + Files.write(path, strings); + } catch (IOException e) { + throw new RuntimeException("Unable to write fireblanket.txt", e); + } + } + + + public static void parse(List text) { + // TODO: proper multiline parsing + // ^([\w|-]+):\s+(.*); + Pattern pattern = Pattern.compile("^([\\w|-]+):\\s+(.*);"); + for (String string : text) { + string = string.strip(); + + if (string.startsWith("#")) { + continue; + } + + Matcher matcher = pattern.matcher(string); + if (matcher.matches()) { + String name = matcher.group(1); + String value = matcher.group(2); + ConfigSpec spec = ConfigSpecs.BY_NAME.get(name); + if (spec == null) { + throw new RuntimeException("Unknown config option " + name); + } + + put(spec, spec.parser().parse(value)); + } + } + + System.out.println(map); + } +} diff --git a/src/main/java/net/modfest/fireblanket/config/ParseException.java b/src/main/java/net/modfest/fireblanket/config/ParseException.java new file mode 100644 index 0000000..3eb12d8 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/ParseException.java @@ -0,0 +1,7 @@ +package net.modfest.fireblanket.config; + +public class ParseException extends RuntimeException { + public ParseException(String in) { + super(in); + } +} diff --git a/src/main/java/net/modfest/fireblanket/config/ParseFunc.java b/src/main/java/net/modfest/fireblanket/config/ParseFunc.java new file mode 100644 index 0000000..38c45d1 --- /dev/null +++ b/src/main/java/net/modfest/fireblanket/config/ParseFunc.java @@ -0,0 +1,6 @@ +package net.modfest.fireblanket.config; + +@FunctionalInterface +public interface ParseFunc { + T parse(String input); +} diff --git a/src/main/java/net/modfest/fireblanket/mixin/client/lambdamap/MixinMapRegionFile.java b/src/main/java/net/modfest/fireblanket/mixin/client/lambdamap/MixinMapRegionFile.java deleted file mode 100644 index 79027f8..0000000 --- a/src/main/java/net/modfest/fireblanket/mixin/client/lambdamap/MixinMapRegionFile.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.modfest.fireblanket.mixin.client.lambdamap; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Coerce; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Pseudo -@Mixin(targets = "dev/lambdaurora/lambdamap/map/storage/MapRegionFile") -public class MixinMapRegionFile { - - @Inject(at = @At("HEAD"), method = "saveChunk", cancellable = true) - public void saveChunk(@Coerce Object mapChunk, CallbackInfo ci) { - // Does DEFLATE on-thread. Laggy as fuck and we don't need it since we're shipping a - // complete map with the pack. - ci.cancel(); - } - -} diff --git a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinDebugStickItem.java b/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinDebugStickItem.java index e2d576f..7d80985 100644 --- a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinDebugStickItem.java +++ b/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinDebugStickItem.java @@ -15,6 +15,7 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Formatting; import net.minecraft.util.Hand; +import net.modfest.fireblanket.compat.roles.Roles; import net.modfest.fireblanket.mixinsupport.ImmmovableLivingEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -44,7 +45,8 @@ public MixinDebugStickItem(Settings settings) { public ActionResult useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand) { NbtCompound nbt = stack.getComponents().getOrDefault(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT).copyNbt(); if (isCustomFireblanket(nbt)) { - if (!user.getWorld().isClient) { + // Only builder+ should be able to use debug hammers + if (!user.getWorld().isClient && Roles.isBuilder(user)) { if (nbt.getBoolean(NOAI)) { if (entity instanceof MobEntity mob) { mob.setAiDisabled(true); diff --git a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinServerChunkLoadingManager.java b/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinServerChunkLoadingManager.java index 11eae85..1f6eb7c 100644 --- a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinServerChunkLoadingManager.java +++ b/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/MixinServerChunkLoadingManager.java @@ -2,7 +2,7 @@ import net.minecraft.entity.EntityType; import net.minecraft.server.world.ServerChunkLoadingManager; -import net.modfest.fireblanket.world.entity.EntityFilters; +import net.modfest.fireblanket.config.EntityFilters; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; diff --git a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/create/MixinSuperGlueEntity.java b/src/main/java/net/modfest/fireblanket/mixin/mods/create/MixinSuperGlueEntity.java similarity index 93% rename from src/main/java/net/modfest/fireblanket/mixin/entity_ticking/create/MixinSuperGlueEntity.java rename to src/main/java/net/modfest/fireblanket/mixin/mods/create/MixinSuperGlueEntity.java index 162f985..198b112 100644 --- a/src/main/java/net/modfest/fireblanket/mixin/entity_ticking/create/MixinSuperGlueEntity.java +++ b/src/main/java/net/modfest/fireblanket/mixin/mods/create/MixinSuperGlueEntity.java @@ -1,4 +1,4 @@ -package net.modfest.fireblanket.mixin.entity_ticking.create; +package net.modfest.fireblanket.mixin.mods.create; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; diff --git a/src/main/java/net/modfest/fireblanket/mixin/client/be_masking/sodium/MixinChunkRenderRebuildTask.java b/src/main/java/net/modfest/fireblanket/mixin/mods/masking/sodium/MixinChunkRenderRebuildTask.java similarity index 97% rename from src/main/java/net/modfest/fireblanket/mixin/client/be_masking/sodium/MixinChunkRenderRebuildTask.java rename to src/main/java/net/modfest/fireblanket/mixin/mods/masking/sodium/MixinChunkRenderRebuildTask.java index 8007cfd..bb7316c 100644 --- a/src/main/java/net/modfest/fireblanket/mixin/client/be_masking/sodium/MixinChunkRenderRebuildTask.java +++ b/src/main/java/net/modfest/fireblanket/mixin/mods/masking/sodium/MixinChunkRenderRebuildTask.java @@ -1,4 +1,4 @@ -package net.modfest.fireblanket.mixin.client.be_masking.sodium; +package net.modfest.fireblanket.mixin.mods.masking.sodium; import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers; import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildContext; diff --git a/src/main/java/net/modfest/fireblanket/mixin/region_chunk_cache/MixinServerChunkManager.java b/src/main/java/net/modfest/fireblanket/mixin/region_chunk_cache/MixinServerChunkManager.java index b3a2ce4..de8c55c 100644 --- a/src/main/java/net/modfest/fireblanket/mixin/region_chunk_cache/MixinServerChunkManager.java +++ b/src/main/java/net/modfest/fireblanket/mixin/region_chunk_cache/MixinServerChunkManager.java @@ -15,6 +15,8 @@ import net.minecraft.world.chunk.ChunkStatusChangeListener; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.level.storage.LevelStorage; +import net.modfest.fireblanket.config.ConfigSpecs; +import net.modfest.fireblanket.config.FireblanketConfig; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -65,7 +67,7 @@ public abstract class MixinServerChunkManager { // Will be real due to mixin plugin if (this.world.getRegistryKey().equals(World.OVERWORLD)) { - int radius = Integer.getInteger("fireblanket.loadRadius"); + int radius = FireblanketConfig.get(ConfigSpecs.FORCED_LOAD_RADIUS); int min = (int) Math.floor(-radius / 16); int max = (int) Math.ceil(radius / 16); diff --git a/src/main/java/net/modfest/fireblanket/world/WorldLoadAppliers.java b/src/main/java/net/modfest/fireblanket/world/WorldLoadAppliers.java index f362d16..a52b5dc 100644 --- a/src/main/java/net/modfest/fireblanket/world/WorldLoadAppliers.java +++ b/src/main/java/net/modfest/fireblanket/world/WorldLoadAppliers.java @@ -1,7 +1,7 @@ package net.modfest.fireblanket.world; import net.modfest.fireblanket.world.blocks.FlatBlockstateArray; -import net.modfest.fireblanket.world.entity.EntityFilters; +import net.modfest.fireblanket.config.EntityFilters; public class WorldLoadAppliers { private static boolean ran = false; diff --git a/src/main/java/net/modfest/fireblanket/world/blocks/FlatBlockstateArray.java b/src/main/java/net/modfest/fireblanket/world/blocks/FlatBlockstateArray.java index c8ce2dd..4f9e3ee 100644 --- a/src/main/java/net/modfest/fireblanket/world/blocks/FlatBlockstateArray.java +++ b/src/main/java/net/modfest/fireblanket/world/blocks/FlatBlockstateArray.java @@ -9,6 +9,11 @@ public class FlatBlockstateArray { public static void apply() { int size = Block.STATE_IDS.size(); + if (size > 1048575) { + throw new IllegalStateException("Fireblanket cannot start! We're attempting to load " + size + " unique blockstates" + + ", but we can only support up to 1048575! Please disable the flatten-chunk-palettes option to continue."); + } + FROM_ID = new BlockState[size]; int i = 0; for (BlockState b : Block.STATE_IDS) { diff --git a/src/main/java/net/modfest/fireblanket/world/blocks/UpdateSignBlockEntityTypes.java b/src/main/java/net/modfest/fireblanket/world/blocks/UpdateSignBlockEntityTypes.java index 9927bec..eafe647 100644 --- a/src/main/java/net/modfest/fireblanket/world/blocks/UpdateSignBlockEntityTypes.java +++ b/src/main/java/net/modfest/fireblanket/world/blocks/UpdateSignBlockEntityTypes.java @@ -14,26 +14,30 @@ public class UpdateSignBlockEntityTypes { public static void apply(Block block) { - if (block instanceof WallHangingSignBlock || block instanceof HangingSignBlock) { - BlockEntityTypeAccessor sign = (BlockEntityTypeAccessor) BlockEntityType.HANGING_SIGN; + try { + if (block instanceof WallHangingSignBlock || block instanceof HangingSignBlock) { + BlockEntityTypeAccessor sign = (BlockEntityTypeAccessor) BlockEntityType.HANGING_SIGN; - if (!(sign.getBlocks() instanceof HashSet)) { - sign.setBlocks(new HashSet<>(sign.getBlocks())); + if (!(sign.getBlocks() instanceof HashSet)) { + sign.setBlocks(new HashSet<>(sign.getBlocks())); + } + + sign.getBlocks().add(block); + Fireblanket.LOGGER.debug("Force-registered a hanging sign block entity: " + Registries.BLOCK.getId(block)); } - sign.getBlocks().add(block); - Fireblanket.LOGGER.debug("Force-registered a hanging sign block entity: " + Registries.BLOCK.getId(block)); - } + if (block instanceof SignBlock || block instanceof WallSignBlock) { + BlockEntityTypeAccessor sign = (BlockEntityTypeAccessor) BlockEntityType.SIGN; - if (block instanceof SignBlock || block instanceof WallSignBlock) { - BlockEntityTypeAccessor sign = (BlockEntityTypeAccessor) BlockEntityType.SIGN; + if (!(sign.getBlocks() instanceof HashSet)) { + sign.setBlocks(new HashSet<>(sign.getBlocks())); + } - if (!(sign.getBlocks() instanceof HashSet)) { - sign.setBlocks(new HashSet<>(sign.getBlocks())); + sign.getBlocks().add(block); + Fireblanket.LOGGER.debug("Force-registered a sign block entity: " + Registries.BLOCK.getId(block)); } - - sign.getBlocks().add(block); - Fireblanket.LOGGER.debug("Force-registered a sign block entity: " + Registries.BLOCK.getId(block)); + } catch (Exception e) { + e.printStackTrace(); } } } diff --git a/src/main/resources/fireblanket.mixins.json b/src/main/resources/fireblanket.mixins.json index d029124..822e8aa 100644 --- a/src/main/resources/fireblanket.mixins.json +++ b/src/main/resources/fireblanket.mixins.json @@ -23,7 +23,7 @@ "entity_ticking.MixinLivingEntity", "entity_ticking.MixinMinecraftServer", "entity_ticking.MixinServerChunkLoadingManager", - "entity_ticking.create.MixinSuperGlueEntity", + "mods.create.MixinSuperGlueEntity", "footgun.MixinEntitySelectorOptions", "footgun.MixinEntitySelectorReader", "fsc.MixinClientConnection", @@ -61,14 +61,13 @@ "client.adventure_fix.MixinClientPlayerInteractionManager", "client.be_masking.MixinBlockEntityRenderDispatcher", "client.be_masking.MixinRebuildTask", - "client.be_masking.sodium.MixinChunkRenderRebuildTask", + "mods.masking.sodium.MixinChunkRenderRebuildTask", "client.bufferbuilder_opto.MixinBufferBuilder", "client.bufferbuilder_opto.MixinVertexFormat", "client.entity_masking.MixinEntityRenderer", "client.entity_ticking.MixinArmorStandEntity", "client.entity_ticking.MixinLivingEntity", "client.hooks.MixinWorldRenderer", - "client.lambdamap.MixinMapRegionFile", "client.render_regions.MixinBlockEntityRenderDispatcher", "client.render_regions.MixinEntityRenderDispatcher", "client.render_regions.MixinRegionSubjects",