Skip to content

Commit

Permalink
Button options (#11)
Browse files Browse the repository at this point in the history
- The menu buttons are back to their original position by default.
- Added an option to move or remove the menu buttons from the Options
screen via ModMenu.
- The menu buttons are now accessible via Mod Menu.
  • Loading branch information
Estecka authored Jul 30, 2024
1 parent 6182d0f commit 9566294
Show file tree
Hide file tree
Showing 22 changed files with 669 additions and 141 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ classes/
.settings/
.vscode/
bin/
.factorypath
.classpath
.project

Expand Down
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
# Data-Pack / Game-Rule Menus

Makes the titular menus accessible from the pause menu in singleplayer worlds.
Takes the titular menus from the World-Creation menu, and makes them available from already created singleplayer worlds.
This provides a more user-friendly alternative to the vanilla `/gamerule` and `/datapack` commands.

Like those commands, the menus will only be accessible in worlds with cheats/commands enabled.

Unlike the vanilla `/datapack` command, the Datapack menu from this mod can be used to toggle experimental features!


## Word of caution: Datapacks

Minecraft's own built-in datapacks are not "just" datapacks, and require special handling. A warning screen will be shown whenever you attempt to toggle any of these, and give you the option to back out.
Some types of datapacks require a world restart to fully take effect.
Whenever possible, the mod will show a warning after toggling one of them, and offer you the option to either back-out, or exit the world gracefully.
However, some type of packs cannot be detected, and no warning will be displayed for those.

### Registry Packs
The new type of packs introduced in MC 1.21. Packs that add data to registries (painting variants, etc), **are not detected by the mod**, but still require a world restart to fully take effects.

### Experimental Feature
When packs that include experimental features (such as bundles) are toggled, the approriate feature flag will be toggled on the world.
Toggling these packs may cause some errors in the log, but those are benign so long as you restart the world immediately afterward. This behaviour is no different from using the `/datapack` command.

However, **unlike regular datapacks, those won't fully take effect until the world is restarted.** After you confirm the changes, the world will immediately exit.
### Experimental Features
Packs that include experimental features (such as bundles) are properly detected by the mod. Toggling them will also toggle the corresponding feature-flag on the world, and exit the world gracefully.

### Vanilla Datapack
The Vanilla datapack can technically be disabled, but the uses cases for this are very marginal. Doing so will break worlds most of the times, so you probably don't want to do it.
The Vanilla datapack can technically be disabled, but you probably don't want to do it.
Doing so will usually break worlds unless you know exactly what you are doing.
An additional warning screen will appear when trying to disable this pack.

If you can't load a world after having disabled the Vanilla datapack, loading it in Safe Mode should be able to restore it.
If you can't load a world after having disabled the Vanilla datapack, loading it in Safe Mode should be able to restore it.
15 changes: 7 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish'
}

Expand All @@ -18,6 +18,7 @@ repositories {
// for more information about repositories.

maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/releases/" }

maven {
name = "Modrinth"
Expand All @@ -34,11 +35,7 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

// Uncomment the following line to enable the deprecated Fabric API modules.
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.

// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"

modApi "com.terraformersmc:modmenu:${project.modmenu_version}"
modApi("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_version}") {
exclude(group: "net.fabricmc.fabric-api")
}
Expand All @@ -52,8 +49,6 @@ processResources {
filesMatching("fabric.mod.json") {
expand "version": project.version
}

exclude "**/*.xcf"
}

tasks.withType(JavaCompile).configureEach {
Expand All @@ -74,6 +69,10 @@ jar {
from("LICENSE") {
rename { "${it}_${project.base.archivesName.get()}"}
}

exclude "**/*.xcf"
exclude "**/*.psd"
exclude "**/*.psb"
}

// configure the maven publication
Expand Down
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@
### 1.2.2
- Updated for MC1.20.5.
- In 1.20.5, the position of the buttons in the option menu has changed slightly.


# v2
- The menu buttons are back to their original position by default.
- Added an option to move or remove the menu buttons from the Options screen via ModMenu.
- The menu buttons are now accessible via Mod Menu.
19 changes: 10 additions & 9 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ org.gradle.parallel=true

# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20.5
yarn_mappings=1.20.5+build.1
loader_version=0.15.10
minecraft_version=1.21
yarn_mappings=1.21+build.9
loader_version=0.15.11

#Fabric api
fabric_version=0.97.6+1.20.5
# Fabric API
fabric_version=0.100.7+1.21

# Mod Properties
mod_version=1.2.2
maven_group=tk.estecka.packrulemenus
mod_version=2.0.0
maven_group=fr.estecka.packrulemenus
archives_base_name=packrule-menus

# Optional Dependencies
clothGamerules_version=1.0.0+1.19.4
cloth_version=14.0.126
clothGamerules_version=1.1.1+1.19.4
cloth_version=15.0.128
modmenu_version=11.0.1
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
2 changes: 1 addition & 1 deletion gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,81 +1,59 @@
package tk.estecka.packrulemenus.mixin;
package fr.estecka.packrulemenus;

import java.util.Collection;
import java.util.function.Supplier;
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 com.llamalad7.mixinextras.sugar.Local;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.MessageScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.option.OptionsScreen;
import net.minecraft.client.gui.screen.pack.PackScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.GridWidget;
import net.minecraft.resource.DataConfiguration;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.featuretoggle.FeatureFlags;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.WorldSavePath;
import net.minecraft.world.GameRules;
import tk.estecka.packrulemenus.GenericWarningScreen;
import tk.estecka.packrulemenus.PackRuleMenus;

@Unique
@Mixin(OptionsScreen.class)
abstract public class OptionScreenMixin
extends Screen
import fr.estecka.packrulemenus.gui.GenericWarningScreen;
import fr.estecka.packrulemenus.mixin.IMinecraftServerMixin;

public class DatapackHandler
{
private OptionScreenMixin(){ super(null); }

@Shadow abstract ButtonWidget createButton(Text message, Supplier<Screen> screenSupplier);

private IntegratedServer server;

@Inject( method="init", at=@At(value="INVOKE", ordinal=0, target="net/minecraft/client/gui/widget/GridWidget$Adder.add (Lnet/minecraft/client/gui/widget/Widget;)Lnet/minecraft/client/gui/widget/Widget;") )
private void gameruleMenu$Init(CallbackInfo info, @Local GridWidget.Adder adder){
this.server = this.client.getServer();

if (client.isIntegratedServerRunning()
&& server.getSaveProperties().areCommandsAllowed()
&& server.getOverworld() != null)
{
final GameRules worldRules = server.getOverworld().getGameRules();
adder.add(createButton(
Text.translatable("selectWorld.gameRules"),
() -> PackRuleMenus.CreateGameruleScreen(
this,
worldRules.copy(),
optRules -> optRules.ifPresent(r -> worldRules.setAllValues(r, server))
)
));

var rollback = server.getDataPackManager().getEnabledIds();
adder.add(createButton(
Text.translatable("selectWorld.dataPacks"),
() -> new PackScreen(
server.getDataPackManager(),
manager -> { HandleDatapackRefresh(manager, rollback); },
server.getSavePath(WorldSavePath.DATAPACKS),
Text.translatable("dataPack.title")
)
));
}
private final Screen parent;
private final IntegratedServer server;
private final MinecraftClient client = MinecraftClient.getInstance();

public DatapackHandler(Screen parent, IntegratedServer server){
this.parent = parent;
this.server = server;
}

private void RevertScreen(){
client.setScreen((OptionsScreen)(Object)this);
client.setScreen(parent);
};

public ButtonWidget CreateButton(){
return ButtonWidget.builder(
Text.translatable("selectWorld.dataPacks"),
__->client.setScreen( CreateScreen() )
).build();
}

public PackScreen CreateScreen(){
Collection<String> rollback = server.getDataPackManager().getEnabledIds();

return new PackScreen(
server.getDataPackManager(),
manager -> { HandleDatapackRefresh(manager, rollback); },
server.getSavePath(WorldSavePath.DATAPACKS),
Text.translatable("dataPack.title")
);
}

private void HandleDatapackRefresh(final ResourcePackManager manager, Collection<String> rollback){
FeatureSet neoFeatures = manager.getRequestedFeatures();
FeatureSet oldFeatures = server.getSaveProperties().getEnabledFeatures();
Expand All @@ -101,11 +79,11 @@ private void HandleDatapackRefresh(final ResourcePackManager manager, Collection
}
};

client.setScreen(GenericWarningScreen.FeatureWarning(isExperimental, confirmed -> {
client.setScreen(FeatureWarning(isExperimental, confirmed -> {
if (!wasVanillaRemoved || !confirmed)
onConfirm.accept(confirmed);
else
client.setScreen(GenericWarningScreen.VanillaWarning(onConfirm));
client.setScreen(VanillaWarning(onConfirm));
}));
}
}
Expand All @@ -116,7 +94,7 @@ private void ApplyFlags(final ResourcePackManager manager){
String featureNames = "";
for (Identifier id : FeatureFlags.FEATURE_MANAGER.toId(features))
featureNames += id.toString()+", ";
PackRuleMenus.LOGGER.info("Reloading packs with features: {}", featureNames);
PackRuleMod.LOGGER.info("Reloading packs with features: {}", featureNames);

server.getSaveProperties().updateLevelInfo(new DataConfiguration(IMinecraftServerMixin.callCreateDataPackSettings(manager, true), features));
}
Expand All @@ -126,10 +104,31 @@ private void ReloadPacks(final ResourcePackManager manager){
client.inGameHud.getChatHud().addMessage(Text.translatable("commands.reload.success"));

server.reloadResources(manager.getEnabledIds()).exceptionally(e -> {
PackRuleMenus.LOGGER.error("{}", e);
PackRuleMod.LOGGER.error("{}", e);
client.inGameHud.getChatHud().addMessage(Text.translatable("commands.reload.failure").formatted(Formatting.RED));
return null;
});
}

static public GenericWarningScreen FeatureWarning(boolean isExperimental, BooleanConsumer onConfirm){
MutableText msg = Text.translatable("packrulemenus.warning.featureflag.message");
if (isExperimental)
msg.append("\n\n").append(Text.translatable("selectWorld.experimental.message"));

return new GenericWarningScreen(
Text.translatable("packrulemenus.warning.featureflag.title"),
msg,
Text.translatable("packrulemenus.warning.featureflag.checkbox"),
onConfirm
);
}

static public GenericWarningScreen VanillaWarning(BooleanConsumer onConfirm){
return new GenericWarningScreen(
Text.translatable("packrulemenus.warning.vanillapack.title"),
Text.translatable("packrulemenus.warning.vanillapack.message"),
Text.translatable("packrulemenus.warning.vanillapack.checkbox"),
onConfirm
);
}
}
44 changes: 44 additions & 0 deletions src/main/java/fr/estecka/packrulemenus/GameruleHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package fr.estecka.packrulemenus;

import java.util.Optional;
import java.util.function.Consumer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.world.EditGameRulesScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.text.Text;
import net.minecraft.world.GameRules;
import tk.estecka.clothgamerules.api.ClothGamerulesScreenFactory;

public class GameruleHandler
{
private final Screen parent;
private final IntegratedServer server;
private final MinecraftClient client = MinecraftClient.getInstance();

public GameruleHandler(Screen parent, IntegratedServer server){
this.parent = parent;
this.server = server;
}

public ButtonWidget CreateButton() {
final GameRules worldRules = server.getOverworld().getGameRules();

return ButtonWidget.builder(
Text.translatable("selectWorld.gameRules"),
__ -> client.setScreen( CreateGameruleScreen(parent, worldRules.copy(), optRules -> optRules.ifPresent(r -> worldRules.setAllValues(r, server))) )
)
.build()
;
}


static public Screen CreateGameruleScreen(Screen parent, GameRules rules, Consumer<Optional<GameRules>> saveConsumer){
if (FabricLoader.getInstance().isModLoaded("cloth-gamerules"))
return ClothGamerulesScreenFactory.CreateScreen(parent, rules, saveConsumer);
else
return new EditGameRulesScreen(rules, saveConsumer.andThen( __->MinecraftClient.getInstance().setScreen(parent) ));
}
}
Loading

0 comments on commit 9566294

Please sign in to comment.