From 61082d00ef93128f5c81e28bb5346f66e85b8bce Mon Sep 17 00:00:00 2001 From: jessy-david-dev Date: Thu, 11 Jun 2026 17:47:01 +0200 Subject: [PATCH] Initial commit - DimensionAccess 1.0.0 --- .gitignore | 21 +++ LICENSE.txt | 2 + build.gradle | 105 +++++++++++++++ gradle.properties | 15 +++ gradle/wrapper/gradle-wrapper.properties | 1 + settings.gradle | 9 ++ .../dimension_access/DimensionAccessMod.java | 35 +++++ .../command/DimensionCommand.java | 124 ++++++++++++++++++ .../config/DimensionAccessConfig.java | 102 ++++++++++++++ .../mixin/ServerPlayerEntityMixin.java | 42 ++++++ .../resources/dimension_access.mixins.json | 11 ++ src/main/resources/fabric.mod.json | 29 ++++ 12 files changed, 496 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle create mode 100644 src/main/java/fr/jessydavid/dimension_access/DimensionAccessMod.java create mode 100644 src/main/java/fr/jessydavid/dimension_access/command/DimensionCommand.java create mode 100644 src/main/java/fr/jessydavid/dimension_access/config/DimensionAccessConfig.java create mode 100644 src/main/java/fr/jessydavid/dimension_access/mixin/ServerPlayerEntityMixin.java create mode 100644 src/main/resources/dimension_access.mixins.json create mode 100644 src/main/resources/fabric.mod.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e4b940 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# MacOS DS_Store files +.DS_Store + +# Gradle cache folder +.gradle + +# Gradle build folder +build + +# IntelliJ +out/ +.idea +*.iml +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Common working directory +run diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..05443aa --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,2 @@ +Copyright (c) 2026 +All rights reserved. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..091fd7e --- /dev/null +++ b/build.gradle @@ -0,0 +1,105 @@ +plugins { + id 'fabric-loom' version '1.17-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name +} + +loom { + splitEnvironmentSourceSets() + + mods { + "dimension_access" { + sourceSet sourceSets.main + sourceSet sourceSets.client + } + } +} + +fabricApi { + configureDataGeneration { + client = true + } +} + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" +} + +processResources { + inputs.property "version", project.version + inputs.property "minecraft_version", project.minecraft_version + inputs.property "loader_version", project.loader_version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version, + "minecraft_version": project.minecraft_version, + "loader_version": project.loader_version + } +} + +def targetJavaVersion = 21 +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release.set(targetJavaVersion) + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archives_base_name}" } + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava", MavenPublication) { + artifactId = project.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..88eea89 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.21.1 +yarn_mappings=1.21.1+build.3 +loader_version=0.18.4 +# Mod Properties +mod_version=1.0 +maven_group=fr.jessydavid +archives_base_name=DimensionAccess +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.116.12+1.21.1 +loom.accessWidener= \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cead174 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/fr/jessydavid/dimension_access/DimensionAccessMod.java b/src/main/java/fr/jessydavid/dimension_access/DimensionAccessMod.java new file mode 100644 index 0000000..89ce625 --- /dev/null +++ b/src/main/java/fr/jessydavid/dimension_access/DimensionAccessMod.java @@ -0,0 +1,35 @@ +package fr.jessydavid.dimension_access; + +import fr.jessydavid.dimension_access.command.DimensionCommand; +import fr.jessydavid.dimension_access.config.DimensionAccessConfig; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DimensionAccessMod implements ModInitializer { + + public static final String MOD_ID = "dimension_access"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + + private static DimensionAccessConfig config; + + @Override + public void onInitialize() { + LOGGER.info("[DimensionAccess] Initializing..."); + + config = new DimensionAccessConfig(); + config.load(); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> + DimensionCommand.register(dispatcher, config) + ); + + LOGGER.info("[DimensionAccess] Ready. {} dimension(s) disabled.", + config.getDisabledDimensions().size()); + } + + public static DimensionAccessConfig getConfig() { + return config; + } +} diff --git a/src/main/java/fr/jessydavid/dimension_access/command/DimensionCommand.java b/src/main/java/fr/jessydavid/dimension_access/command/DimensionCommand.java new file mode 100644 index 0000000..cd2564c --- /dev/null +++ b/src/main/java/fr/jessydavid/dimension_access/command/DimensionCommand.java @@ -0,0 +1,124 @@ +package fr.jessydavid.dimension_access.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import fr.jessydavid.dimension_access.config.DimensionAccessConfig; +import net.minecraft.command.CommandSource; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; + +public class DimensionCommand { + + private static DimensionAccessConfig config; + + public static void register(CommandDispatcher dispatcher, + DimensionAccessConfig cfg) { + config = cfg; + + dispatcher.register( + CommandManager.literal("dimension") + .requires(source -> source.hasPermissionLevel(2)) + + .then(CommandManager.literal("disable") + .then(CommandManager.argument("dimension", StringArgumentType.string()) + .suggests(knownDimensionSuggestions()) + .executes(ctx -> executeDisable(ctx, + StringArgumentType.getString(ctx, "dimension"))) + ) + ) + + .then(CommandManager.literal("enable") + .then(CommandManager.argument("dimension", StringArgumentType.string()) + .suggests(disabledDimensionSuggestions()) + .executes(ctx -> executeEnable(ctx, + StringArgumentType.getString(ctx, "dimension"))) + ) + ) + + .then(CommandManager.literal("reload") + .executes(DimensionCommand::executeReload) + ) + ); + } + + private static int executeDisable(CommandContext ctx, String dimensionId) { + ServerCommandSource source = ctx.getSource(); + + if (Identifier.tryParse(dimensionId) == null) { + source.sendError(Text.literal("Invalid dimension identifier: " + dimensionId)); + return 0; + } + + if (config.disableDimension(dimensionId)) { + source.sendFeedback( + () -> Text.literal("✓ Dimension ").formatted(Formatting.GREEN) + .append(Text.literal(dimensionId).formatted(Formatting.YELLOW)) + .append(Text.literal(" disabled.").formatted(Formatting.GREEN)), + true + ); + return 1; + } else { + source.sendError(Text.literal("Dimension " + dimensionId + " is already disabled.")); + return 0; + } + } + + private static int executeEnable(CommandContext ctx, String dimensionId) { + ServerCommandSource source = ctx.getSource(); + + if (Identifier.tryParse(dimensionId) == null) { + source.sendError(Text.literal("Invalid dimension identifier: " + dimensionId)); + return 0; + } + + if (config.enableDimension(dimensionId)) { + source.sendFeedback( + () -> Text.literal("✓ Dimension ").formatted(Formatting.GREEN) + .append(Text.literal(dimensionId).formatted(Formatting.YELLOW)) + .append(Text.literal(" enabled.").formatted(Formatting.GREEN)), + true + ); + return 1; + } else { + source.sendError(Text.literal("Dimension " + dimensionId + " is not currently disabled.")); + return 0; + } + } + + private static int executeReload(CommandContext ctx) { + config.load(); + ServerCommandSource source = ctx.getSource(); + source.sendFeedback( + () -> Text.literal("✓ Config reloaded. ").formatted(Formatting.GREEN) + .append(Text.literal(config.getDisabledDimensions().size() + " dimension(s) disabled.") + .formatted(Formatting.GRAY)), + true + ); + return 1; + } + + private static SuggestionProvider knownDimensionSuggestions() { + return (ctx, builder) -> { + CommandSource.suggestMatching(new String[]{ + "minecraft:overworld", + "minecraft:the_nether", + "minecraft:the_end" + }, builder); + ctx.getSource().getServer().getWorldRegistryKeys() + .forEach(key -> builder.suggest(key.getValue().toString())); + return builder.buildFuture(); + }; + } + + private static SuggestionProvider disabledDimensionSuggestions() { + return (ctx, builder) -> { + CommandSource.suggestMatching(config.getDisabledDimensions(), builder); + return builder.buildFuture(); + }; + } +} diff --git a/src/main/java/fr/jessydavid/dimension_access/config/DimensionAccessConfig.java b/src/main/java/fr/jessydavid/dimension_access/config/DimensionAccessConfig.java new file mode 100644 index 0000000..190e1c7 --- /dev/null +++ b/src/main/java/fr/jessydavid/dimension_access/config/DimensionAccessConfig.java @@ -0,0 +1,102 @@ +package fr.jessydavid.dimension_access.config; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import fr.jessydavid.dimension_access.DimensionAccessMod; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class DimensionAccessConfig { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final String CONFIG_FILE = "dimension_access.json"; + + private final Path configPath; + private final Set disabledDimensions = Collections.synchronizedSet(new HashSet<>()); + + public DimensionAccessConfig() { + this.configPath = FabricLoader.getInstance().getConfigDir().resolve(CONFIG_FILE); + } + + public void load() { + if (!Files.exists(configPath)) { + DimensionAccessMod.LOGGER.info("[DimensionAccess] No config found, creating default at: {}", configPath); + save(); + return; + } + + try (Reader reader = Files.newBufferedReader(configPath)) { + JsonObject root = JsonParser.parseReader(reader).getAsJsonObject(); + JsonArray disabled = root.getAsJsonArray("disabled_dimensions"); + + synchronized (disabledDimensions) { + disabledDimensions.clear(); + if (disabled != null) { + disabled.forEach(el -> disabledDimensions.add(el.getAsString())); + } + } + + DimensionAccessMod.LOGGER.info("[DimensionAccess] Config loaded: {} dimension(s) disabled.", + disabledDimensions.size()); + } catch (IOException e) { + DimensionAccessMod.LOGGER.error("[DimensionAccess] Failed to load config: {}", e.getMessage()); + } + } + + public void save() { + JsonObject root = new JsonObject(); + JsonArray disabled = new JsonArray(); + + synchronized (disabledDimensions) { + disabledDimensions.stream().sorted().forEach(disabled::add); + } + + root.add("disabled_dimensions", disabled); + + try { + Files.createDirectories(configPath.getParent()); + try (Writer writer = Files.newBufferedWriter(configPath)) { + GSON.toJson(root, writer); + } + } catch (IOException e) { + DimensionAccessMod.LOGGER.error("[DimensionAccess] Failed to save config: {}", e.getMessage()); + } + } + + public boolean disableDimension(String dimensionId) { + boolean added = disabledDimensions.add(dimensionId); + if (added) save(); + return added; + } + + public boolean enableDimension(String dimensionId) { + boolean removed = disabledDimensions.remove(dimensionId); + if (removed) save(); + return removed; + } + + public boolean isDimensionDisabled(String dimensionId) { + return disabledDimensions.contains(dimensionId); + } + + public Set getDisabledDimensions() { + synchronized (disabledDimensions) { + return Collections.unmodifiableSet(new HashSet<>(disabledDimensions)); + } + } + + public Path getConfigPath() { + return configPath; + } +} diff --git a/src/main/java/fr/jessydavid/dimension_access/mixin/ServerPlayerEntityMixin.java b/src/main/java/fr/jessydavid/dimension_access/mixin/ServerPlayerEntityMixin.java new file mode 100644 index 0000000..37fc562 --- /dev/null +++ b/src/main/java/fr/jessydavid/dimension_access/mixin/ServerPlayerEntityMixin.java @@ -0,0 +1,42 @@ +package fr.jessydavid.dimension_access.mixin; + +import fr.jessydavid.dimension_access.DimensionAccessMod; +import net.minecraft.entity.Entity; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.world.TeleportTarget; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ServerPlayerEntity.class) +public abstract class ServerPlayerEntityMixin { + + @Inject( + method = "teleportTo(Lnet/minecraft/world/TeleportTarget;)Lnet/minecraft/entity/Entity;", + at = @At("HEAD"), + cancellable = true + ) + private void onTeleportTo(TeleportTarget target, CallbackInfoReturnable cir) { + ServerPlayerEntity player = (ServerPlayerEntity) (Object) this; + String destinationId = target.world().getRegistryKey().getValue().toString(); + + if (DimensionAccessMod.getConfig().isDimensionDisabled(destinationId)) { + DimensionAccessMod.LOGGER.info( + "[DimensionAccess] Blocking '{}' from entering disabled dimension '{}'.", + player.getName().getString(), destinationId); + + player.sendMessage( + Text.literal("✗ Access to dimension ") + .formatted(Formatting.RED) + .append(Text.literal(destinationId).formatted(Formatting.YELLOW)) + .append(Text.literal(" is currently disabled.").formatted(Formatting.RED)), + false + ); + + cir.setReturnValue(null); + } + } +} diff --git a/src/main/resources/dimension_access.mixins.json b/src/main/resources/dimension_access.mixins.json new file mode 100644 index 0000000..1f9f2ab --- /dev/null +++ b/src/main/resources/dimension_access.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "fr.jessydavid.dimension_access.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "ServerPlayerEntityMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..af91aee --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "id": "dimension_access", + "version": "${version}", + "name": "Dimension Access", + "description": "", + "authors": [], + "contact": {}, + "license": "All-Rights-Reserved", + "icon": "assets/dimension_access/icon.png", + "environment": "server", + "entrypoints": { + "main": [ + "fr.jessydavid.dimension_access.DimensionAccessMod" + ] + }, + "mixins": [ + "dimension_access.mixins.json", + { + "config": "dimension_access.client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=${loader_version}", + "fabric-api": "*", + "minecraft": "${minecraft_version}" + } +}