From 8a4689976920ad29141a9b68491dfdc95fa4cf3b Mon Sep 17 00:00:00 2001 From: hypherionmc Date: Tue, 14 Jan 2025 17:14:57 +0200 Subject: [PATCH] [DEV] New Cloth Config GUIs, new nojang apis, and bug fixes --- 1.18.2/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 8 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 411 ++++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 129 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.18.2/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.18.2/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 16 +- 1.18.2/changelog.md | 13 +- 1.18.2/gradle.properties | 5 +- 1.19.2/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.19.2/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.19.2/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 16 +- 1.19.2/changelog.md | 13 +- 1.19.2/gradle.properties | 5 +- 1.19.3/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.19.3/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.19.3/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 18 +- 1.19.3/changelog.md | 13 +- 1.19.3/gradle.properties | 5 +- 1.20.2/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.20.2/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.20.2/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 18 +- 1.20.2/changelog.md | 13 +- 1.20.2/gradle.properties | 5 +- 1.20.4/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.20.4/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.20.4/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 17 +- 1.20.4/NeoForge/build.gradle | 10 + .../client/NeoForgeClientHelper.java | 1 + 1.20.4/changelog.md | 13 +- 1.20.4/gradle.properties | 5 +- 1.20/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.20/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.20/Forge/build.gradle | 10 + .../mixin/ConfigScreenHandlerMixin.java | 17 +- 1.20/changelog.md | 13 +- 1.20/gradle.properties | 5 +- 1.21.2/.jenkins/Jenkinsfile.snapshot | 2 +- 1.21.2/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.21.2/Fabric/build.gradle | 7 +- .../CraterLibModMenuIntegration.java | 14 +- 1.21.2/NeoForge/build.gradle | 14 +- .../client/NeoForgeClientHelper.java | 14 +- 1.21.2/Paper/build.gradle | 2 +- 1.21.2/changelog.md | 13 +- 1.21.2/gradle.properties | 5 +- 1.21/Common/build.gradle | 2 + .../craterlib/api/commands/CraterCommand.java | 12 +- .../events/compat/LuckPermsCompatEvents.java | 3 + .../gui/config/ClothConfigScreenBuilder.java | 409 +++++++++++++++++ .../client/gui/config/CraterConfigScreen.java | 1 + .../config/widgets/AbstractConfigWidget.java | 1 + .../client/gui/config/widgets/BaseWidget.java | 1 + .../widgets/ClothConfigButtonEntry.java | 128 ++++++ .../config/widgets/InternalConfigButton.java | 1 + .../client/gui/config/widgets/Option.java | 1 + .../gui/config/widgets/SubConfigWidget.java | 1 + .../gui/config/widgets/TextConfigOption.java | 1 + .../gui/config/widgets/ToggleButton.java | 1 + .../gui/config/widgets/WrappedEditBox.java | 1 + .../core/config/annotations/ClothScreen.java | 12 + .../nojang/client/gui/BridgedScreen.java | 13 +- .../world/entity/player/BridgedPlayer.java | 29 ++ .../assets/craterlib/lang/en_us.json | 6 +- 1.21/Fabric/build.gradle | 3 + .../CraterLibModMenuIntegration.java | 14 +- 1.21/NeoForge/build.gradle | 10 + .../client/NeoForgeClientHelper.java | 14 +- 1.21/changelog.md | 13 +- 1.21/gradle.properties | 5 +- commit.sha | 2 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- patches/1.18.2/Common/build.gradle.patch | 4 +- .../api/commands/CraterCommand.java.patch | 28 +- .../ClothConfigScreenBuilder.java.patch | 120 +++++ .../gui/config/CraterConfigScreen.java.patch | 20 +- .../widgets/AbstractConfigWidget.java.patch | 2 +- .../gui/config/widgets/BaseWidget.java.patch | 6 +- .../widgets/ClothConfigButtonEntry.java.patch | 67 +++ .../widgets/InternalConfigButton.java.patch | 2 +- .../gui/config/widgets/Option.java.patch | 2 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../widgets/TextConfigOption.java.patch | 2 +- .../config/widgets/ToggleButton.java.patch | 2 +- patches/1.18.2/Fabric/build.gradle.patch | 8 +- patches/1.18.2/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 34 +- patches/1.18.2/NeoForge/build.gradle.patch | 16 +- .../client/NeoForgeClientHelper.java.patch | 16 +- patches/1.18.2/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 14 + .../gui/config/CraterConfigScreen.java.patch | 16 +- .../widgets/AbstractConfigWidget.java.patch | 2 +- .../gui/config/widgets/BaseWidget.java.patch | 6 +- .../widgets/ClothConfigButtonEntry.java.patch | 62 +++ .../widgets/InternalConfigButton.java.patch | 4 +- .../gui/config/widgets/Option.java.patch | 2 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../widgets/TextConfigOption.java.patch | 2 +- .../config/widgets/ToggleButton.java.patch | 2 +- patches/1.19.2/Fabric/build.gradle.patch | 8 +- patches/1.19.2/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 35 +- patches/1.19.2/NeoForge/build.gradle.patch | 16 +- .../client/NeoForgeClientHelper.java.patch | 16 +- patches/1.19.2/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 14 + .../gui/config/CraterConfigScreen.java.patch | 16 +- .../widgets/AbstractConfigWidget.java.patch | 2 +- .../gui/config/widgets/BaseWidget.java.patch | 4 +- .../widgets/ClothConfigButtonEntry.java.patch | 34 ++ .../widgets/InternalConfigButton.java.patch | 4 +- .../gui/config/widgets/Option.java.patch | 2 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../widgets/TextConfigOption.java.patch | 2 +- patches/1.19.3/Fabric/build.gradle.patch | 8 +- patches/1.19.3/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 41 +- patches/1.19.3/NeoForge/build.gradle.patch | 16 +- .../client/NeoForgeClientHelper.java.patch | 16 +- patches/1.19.3/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 14 + .../gui/config/CraterConfigScreen.java.patch | 8 +- patches/1.20.2/Fabric/build.gradle.patch | 8 +- patches/1.20.2/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 41 +- patches/1.20.2/NeoForge/build.gradle.patch | 16 +- .../client/NeoForgeClientHelper.java.patch | 16 +- patches/1.20.2/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 11 + .../gui/config/CraterConfigScreen.java.patch | 8 +- patches/1.20.4/Fabric/build.gradle.patch | 6 +- patches/1.20.4/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 37 +- patches/1.20.4/NeoForge/build.gradle.patch | 10 +- .../client/NeoForgeClientHelper.java.patch | 18 +- patches/1.20.4/gradle.properties.patch | 7 +- .../1.20/.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 14 + .../gui/config/CraterConfigScreen.java.patch | 14 +- .../widgets/InternalConfigButton.java.patch | 4 +- patches/1.20/Fabric/build.gradle.patch | 8 +- patches/1.20/Forge/build.gradle.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 37 +- patches/1.20/NeoForge/build.gradle.patch | 16 +- .../client/NeoForgeClientHelper.java.patch | 16 +- patches/1.20/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- patches/1.21.2/Forge/build.gradle.patch | 12 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 17 +- patches/1.21.2/NeoForge/build.gradle.patch | 2 +- patches/1.21.2/gradle.properties.patch | 2 +- .../1.21/.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../ClothConfigScreenBuilder.java.patch | 11 + .../gui/config/CraterConfigScreen.java.patch | 6 +- patches/1.21/Fabric/build.gradle.patch | 6 +- patches/1.21/Forge/build.gradle.patch | 12 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 17 +- patches/1.21/NeoForge/build.gradle.patch | 8 +- patches/1.21/gradle.properties.patch | 11 +- 290 files changed, 6213 insertions(+), 459 deletions(-) create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch create mode 100644 patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch create mode 100644 patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch create mode 100644 patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch create mode 100644 patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch diff --git a/1.18.2/Common/build.gradle b/1.18.2/Common/build.gradle index af5315c..2dd0346 100644 --- a/1.18.2/Common/build.gradle +++ b/1.18.2/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") + } shadowJar { diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index ee66687..d757b42 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -138,9 +139,14 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { + try { if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !(stack.getEntity() instanceof Player) || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + return stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } return LuckPermsCompat.INSTANCE.hasPermission((ServerPlayer) stack.getEntity(), this.luckPermNode) || stack.hasPermission(this.permLevel); } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..be7b13c --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,411 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(new TextComponent("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + new TranslatableComponent("cl.buttons.entry", (i + 1)), + new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + new TextComponent(""), + new TranslatableComponent("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(new TextComponent("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + new TranslatableComponent("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = new TextComponent(""); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = new TextComponent(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = new TextComponent(""); + + for (String comment : tooltip.value()) { + c.getSiblings().add(new TextComponent(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return new TranslatableComponent(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + new TextComponent("Failed To Save Config"), + new TextComponent("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 8b3da68..1a3f739 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -38,6 +38,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index 408c515..01c1ef8 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index ab45fb1..a4abdf4 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextComponent; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..fbe4e4d --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,129 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = new Button(0, 0, mainButtonWidth, 20, fieldName, onPress); + this.deleteButton = deletePress != null ? new Button(0, 0, 20, 20, new TextComponent("X"), deletePress) : null; + this.displayName = displayName; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.x = x; + if (hasDeleteButton) { + this.deleteButton.x = x + this.button.getWidth() + 4; + } + } else { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.x = x + entryWidth - this.button.getWidth() - 24; + this.deleteButton.x = x + entryWidth - 20; + } else { + this.button.x = x + entryWidth - this.button.getWidth(); + } + } + + button.y = y + (entryHeight - 20) / 2; + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.y = y + (entryHeight - 20) / 2; + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index fc5d271..ff9d7d1 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index 9573bd0..86efdd9 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index db60620..225cc48 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.TranslatableComponent; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index 192da97..5356c34 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 0efe2d1..2c5f2a4 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -11,6 +11,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.18.2/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.18.2/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.18.2/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.18.2/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.18.2/Fabric/build.gradle b/1.18.2/Fabric/build.gradle index 993c6e7..94abf21 100644 --- a/1.18.2/Fabric/build.gradle +++ b/1.18.2/Fabric/build.gradle @@ -9,6 +9,7 @@ dependencies { exclude(group: "net.fabricmc.fabric-api") } + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -122,9 +123,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.18.2/Forge/build.gradle b/1.18.2/Forge/build.gradle index a375388..9f27438 100644 --- a/1.18.2/Forge/build.gradle +++ b/1.18.2/Forge/build.gradle @@ -5,6 +5,8 @@ dependencies { // Compat modImplementation("maven.modrinth:vanishmod:${vanishmod}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -113,4 +115,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index df8a51a..1ab8067 100644 --- a/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigGuiHandler; @@ -29,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getGuiFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); diff --git a/1.18.2/changelog.md b/1.18.2/changelog.md index 09222a4..c4a47e5 100644 --- a/1.18.2/changelog.md +++ b/1.18.2/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.18.2/gradle.properties b/1.18.2/gradle.properties index b690303..41be151 100644 --- a/1.18.2/gradle.properties +++ b/1.18.2/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -26,6 +26,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=6.5.102 # Mod Dependencies ftb_ranks=1802.1.11-build.71 diff --git a/1.19.2/Common/build.gradle b/1.19.2/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.19.2/Common/build.gradle +++ b/1.19.2/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..2cfa362 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 4345c2d..5132ff3 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -36,6 +36,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index 408c515..01c1ef8 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 07cf984..3f2a64b 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..87befc0 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = new Button(0, 0, mainButtonWidth, 20, fieldName, onPress); + this.deleteButton = deletePress != null ? new Button(0, 0, 20, 20, Component.literal("X"), deletePress) : null; + this.displayName = displayName; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.x = x; + if (hasDeleteButton) { + this.deleteButton.x = x + this.button.getWidth() + 4; + } + } else { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.x = x + entryWidth - this.button.getWidth() - 24; + this.deleteButton.x = x + entryWidth - 20; + } else { + this.button.x = x + entryWidth - this.button.getWidth(); + } + } + + button.y = y + (entryHeight - 20) / 2; + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.y = y + (entryHeight - 20) / 2; + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index 8d22fdf..2ac936f 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index 9573bd0..86efdd9 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 6ead50a..323bd04 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index 192da97..5356c34 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index f7c1bdd..f40c6b9 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.19.2/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.19.2/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.19.2/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.19.2/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.19.2/Fabric/build.gradle b/1.19.2/Fabric/build.gradle index 6596fbe..007cdfd 100644 --- a/1.19.2/Fabric/build.gradle +++ b/1.19.2/Fabric/build.gradle @@ -11,6 +11,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -124,9 +125,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.19.2/Forge/build.gradle b/1.19.2/Forge/build.gradle index cc27bca..b5214b6 100644 --- a/1.19.2/Forge/build.gradle +++ b/1.19.2/Forge/build.gradle @@ -8,6 +8,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -116,4 +118,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index d77853c..466e0f3 100644 --- a/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigScreenHandler; @@ -29,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); diff --git a/1.19.2/changelog.md b/1.19.2/changelog.md index 09222a4..c4a47e5 100644 --- a/1.19.2/changelog.md +++ b/1.19.2/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.19.2/gradle.properties b/1.19.2/gradle.properties index 814d16f..fb4946a 100644 --- a/1.19.2/gradle.properties +++ b/1.19.2/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -26,6 +26,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=8.3.134 # Mod Dependencies ftb_ranks=1902.1.16-build.114 diff --git a/1.19.3/Common/build.gradle b/1.19.3/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.19.3/Common/build.gradle +++ b/1.19.3/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..2cfa362 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 4f0d773..d7c58a0 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -36,6 +36,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index dc7ac98..1ab794e 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 14a0695..0c28649 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..aec3164 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index c4cf1e8..888fc03 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index 9573bd0..86efdd9 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 55b8ba1..1dc0c50 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index 192da97..5356c34 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.19.3/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.19.3/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.19.3/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.19.3/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.19.3/Fabric/build.gradle b/1.19.3/Fabric/build.gradle index 730454c..b679ed0 100644 --- a/1.19.3/Fabric/build.gradle +++ b/1.19.3/Fabric/build.gradle @@ -11,6 +11,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -124,9 +125,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.19.3/Forge/build.gradle b/1.19.3/Forge/build.gradle index fe2ce6b..da35ce4 100644 --- a/1.19.3/Forge/build.gradle +++ b/1.19.3/Forge/build.gradle @@ -8,6 +8,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -116,4 +118,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index 78fb62b..8c5847f 100644 --- a/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,10 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigScreenHandler; @@ -30,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); @@ -40,5 +49,4 @@ public class ConfigScreenHandlerMixin { } }); } - } diff --git a/1.19.3/changelog.md b/1.19.3/changelog.md index 09222a4..c4a47e5 100644 --- a/1.19.3/changelog.md +++ b/1.19.3/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.19.3/gradle.properties b/1.19.3/gradle.properties index 36af91b..4925d03 100644 --- a/1.19.3/gradle.properties +++ b/1.19.3/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -26,6 +26,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=9.1.104 # Mod Dependencies ftb_ranks=1904.1.1-build.82 diff --git a/1.20.2/Common/build.gradle b/1.20.2/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.20.2/Common/build.gradle +++ b/1.20.2/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..2cfa362 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 1132e07..53cba9d 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index ed34f6b..c46ea5d 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 0583e61..088d2a5 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..1e4db60 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} \ No newline at end of file diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index 2478c7d..d93386f 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index f49ca68..3ac9f32 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 056f32c..f97b5bc 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index cc05cec..ef5f955 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.20.2/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.20.2/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.20.2/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.20.2/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.20.2/Fabric/build.gradle b/1.20.2/Fabric/build.gradle index f305cd3..2930d3b 100644 --- a/1.20.2/Fabric/build.gradle +++ b/1.20.2/Fabric/build.gradle @@ -11,6 +11,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -124,9 +125,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.20.2/Forge/build.gradle b/1.20.2/Forge/build.gradle index 2181003..a18efbf 100644 --- a/1.20.2/Forge/build.gradle +++ b/1.20.2/Forge/build.gradle @@ -8,6 +8,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -116,4 +118,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index 78fb62b..8c5847f 100644 --- a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,10 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigScreenHandler; @@ -30,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); @@ -40,5 +49,4 @@ public class ConfigScreenHandlerMixin { } }); } - } diff --git a/1.20.2/changelog.md b/1.20.2/changelog.md index 09222a4..c4a47e5 100644 --- a/1.20.2/changelog.md +++ b/1.20.2/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.20.2/gradle.properties b/1.20.2/gradle.properties index 8870e69..87e934f 100644 --- a/1.20.2/gradle.properties +++ b/1.20.2/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -26,6 +26,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=12.0.137 # Mod Dependencies ftb_ranks=2001.1.3 diff --git a/1.20.4/Common/build.gradle b/1.20.4/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.20.4/Common/build.gradle +++ b/1.20.4/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..06d9bda --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastId.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 1132e07..53cba9d 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index ed34f6b..c46ea5d 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 0583e61..088d2a5 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..1e4db60 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} \ No newline at end of file diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index 2478c7d..d93386f 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index f49ca68..3ac9f32 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 056f32c..f97b5bc 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index cc05cec..ef5f955 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.20.4/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.20.4/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.20.4/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.20.4/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.20.4/Fabric/build.gradle b/1.20.4/Fabric/build.gradle index 571ddd8..4e8c137 100644 --- a/1.20.4/Fabric/build.gradle +++ b/1.20.4/Fabric/build.gradle @@ -12,6 +12,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -125,9 +126,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.20.4/Forge/build.gradle b/1.20.4/Forge/build.gradle index d878cc2..b2cfb8c 100644 --- a/1.20.4/Forge/build.gradle +++ b/1.20.4/Forge/build.gradle @@ -8,6 +8,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -116,4 +118,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index d77853c..8c5847f 100644 --- a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigScreenHandler; @@ -29,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); @@ -39,5 +49,4 @@ public class ConfigScreenHandlerMixin { } }); } - } diff --git a/1.20.4/NeoForge/build.gradle b/1.20.4/NeoForge/build.gradle index 95c6d0b..bede94c 100644 --- a/1.20.4/NeoForge/build.gradle +++ b/1.20.4/NeoForge/build.gradle @@ -7,6 +7,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -115,4 +117,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java index d6c2ef7..59483b7 100644 --- a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java @@ -1,6 +1,7 @@ package com.hypherionmc.craterlib.client; import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; diff --git a/1.20.4/changelog.md b/1.20.4/changelog.md index 09222a4..c4a47e5 100644 --- a/1.20.4/changelog.md +++ b/1.20.4/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.20.4/gradle.properties b/1.20.4/gradle.properties index 65cb3f9..0232cd0 100644 --- a/1.20.4/gradle.properties +++ b/1.20.4/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -29,6 +29,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=13.0.138 # Mod Dependencies fabrictailor=2.3.1 diff --git a/1.20/Common/build.gradle b/1.20/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.20/Common/build.gradle +++ b/1.20/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..2cfa362 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index c4ea3a4..a1c1c3f 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index ed34f6b..c46ea5d 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 0583e61..088d2a5 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..1e4db60 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} \ No newline at end of file diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index c1ee0fd..d50e6a2 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index f49ca68..3ac9f32 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 056f32c..f97b5bc 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index cc05cec..ef5f955 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.20/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.20/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.20/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.20/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.20/Fabric/build.gradle b/1.20/Fabric/build.gradle index e7c944b..ec3c0f4 100644 --- a/1.20/Fabric/build.gradle +++ b/1.20/Fabric/build.gradle @@ -11,6 +11,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -124,9 +125,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.20/Forge/build.gradle b/1.20/Forge/build.gradle index a580c64..7b774ab 100644 --- a/1.20/Forge/build.gradle +++ b/1.20/Forge/build.gradle @@ -8,6 +8,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -116,4 +118,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index d77853c..8c5847f 100644 --- a/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.client.ConfigScreenHandler; @@ -29,9 +32,16 @@ public class ConfigScreenHandlerMixin { @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (config.getModId().equals(selectedMod.getModId())) { + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) + ); + } else { cir.setReturnValue( Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ); @@ -39,5 +49,4 @@ public class ConfigScreenHandlerMixin { } }); } - } diff --git a/1.20/changelog.md b/1.20/changelog.md index 09222a4..c4a47e5 100644 --- a/1.20/changelog.md +++ b/1.20/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.20/gradle.properties b/1.20/gradle.properties index 67c9e83..7ea99ae 100644 --- a/1.20/gradle.properties +++ b/1.20/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -26,6 +26,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=11.1.136 # Mod Dependencies ftb_ranks=2001.1.3 diff --git a/1.21.2/.jenkins/Jenkinsfile.snapshot b/1.21.2/.jenkins/Jenkinsfile.snapshot index dd5bb20..c84b05a 100644 --- a/1.21.2/.jenkins/Jenkinsfile.snapshot +++ b/1.21.2/.jenkins/Jenkinsfile.snapshot @@ -3,7 +3,7 @@ def projectIcon = "https://cdn.modrinth.com/data/Nn8Wasaq/a172c634683a11a2e9ae59 def JDK = "21"; def majorMc = "1.21.2"; def modLoaders = "neoforge|fabric|quilt|paper"; -def supportedMc = "1.21.3"; +def supportedMc = "1.21.3|1.21.4"; def reltype = "snapshot"; pipeline { diff --git a/1.21.2/Common/build.gradle b/1.21.2/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.21.2/Common/build.gradle +++ b/1.21.2/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..192c6ac --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToastManager().addToast( + new SystemToast( + SystemToast.SystemToastId.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 01a7f94..38d0cc6 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index ed34f6b..c46ea5d 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 0583e61..088d2a5 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..1e4db60 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index 2478c7d..d93386f 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index f49ca68..3ac9f32 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 056f32c..f97b5bc 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index cc05cec..ef5f955 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.21.2/Fabric/build.gradle b/1.21.2/Fabric/build.gradle index b3da913..b64c1d0 100644 --- a/1.21.2/Fabric/build.gradle +++ b/1.21.2/Fabric/build.gradle @@ -12,6 +12,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -116,8 +117,8 @@ publisher { setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") - setGameVersions("1.21.3") + setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") + setGameVersions("1.21.3", "1.21.4") setLoaders("fabric", "quilt") setArtifact(remapJar) setCurseEnvironment("both") @@ -125,9 +126,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } \ No newline at end of file diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.21.2/NeoForge/build.gradle b/1.21.2/NeoForge/build.gradle index 3e43053..f6d78e2 100644 --- a/1.21.2/NeoForge/build.gradle +++ b/1.21.2/NeoForge/build.gradle @@ -7,6 +7,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -109,10 +111,18 @@ publisher { setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") - setGameVersions("1.21.3") + setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") + setGameVersions("1.21.3", "1.21.4") setLoaders("neoforge") setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java index e7d9013..eacd3a3 100644 --- a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java @@ -1,12 +1,15 @@ package com.hypherionmc.craterlib.client; import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.hypherionmc.craterlib.core.event.CraterEventBus; import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; import com.hypherionmc.craterlib.nojang.client.BridgedOptions; import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -52,9 +55,14 @@ public class NeoForgeClientHelper implements ClientPlatform { CraterEventBus.INSTANCE.postEvent(event); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); + } else { + //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); } }); } diff --git a/1.21.2/Paper/build.gradle b/1.21.2/Paper/build.gradle index ae51192..aca75c5 100644 --- a/1.21.2/Paper/build.gradle +++ b/1.21.2/Paper/build.gradle @@ -76,4 +76,4 @@ publisher { publishModrinth.dependsOn(shadowJar) publishNightbloom.dependsOn(shadowJar) -build.dependsOn(shadowJar) \ No newline at end of file +build.dependsOn(shadowJar) diff --git a/1.21.2/changelog.md b/1.21.2/changelog.md index 09222a4..c4a47e5 100644 --- a/1.21.2/changelog.md +++ b/1.21.2/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.21.2/gradle.properties b/1.21.2/gradle.properties index f8e5f0f..bbb060d 100644 --- a/1.21.2/gradle.properties +++ b/1.21.2/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -29,6 +29,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=17.0.144 # Mod Dependencies fabrictailor=2.3.1 diff --git a/1.21/Common/build.gradle b/1.21/Common/build.gradle index 81fbb3f..87a50e1 100644 --- a/1.21/Common/build.gradle +++ b/1.21/Common/build.gradle @@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") } shadowJar { diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java index 35f29f0..9e3d459 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -1,5 +1,6 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.compat.LuckPermsCompat; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; @@ -137,10 +138,15 @@ public class CraterCommand { } private boolean checkPermission(CommandSourceStack stack) { - if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + try { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); - return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } } @FunctionalInterface diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java index 69e75ad..0057e4d 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/compat/LuckPermsCompatEvents.java @@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat; import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.UUID; @@ -9,6 +10,7 @@ import java.util.UUID; public class LuckPermsCompatEvents { @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupAddedEvent extends CraterEvent { private final String identifier; private final UUID uuid; @@ -20,6 +22,7 @@ public class LuckPermsCompatEvents { } @RequiredArgsConstructor(staticName = "of") + @Getter public static class GroupRemovedEvent extends CraterEvent { private final String identifier; private final UUID uuid; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java new file mode 100644 index 0000000..06d9bda --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java @@ -0,0 +1,409 @@ +package com.hypherionmc.craterlib.client.gui.config; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +/** + * @author HypherionSA + * A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +@ApiStatus.Internal +public class ClothConfigScreenBuilder { + + /** + * Build a new Cloth Config screen, from an {@link AbstractConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, config, null)); + + readConfigFields(config, config, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new sub-screen for config entries marked with {@link SubConfig} + * + * @param config The {@link AbstractConfig} the config screen is for + * @param clazz The object or class that the screen is being built for + * @param parent The parent {@link Screen} this screen will return to when closed + * @return A fully usable Cloth Config screen + */ + private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, clazz, null)); + + readConfigFields(config, clazz, builder); + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * Build a new screen, that allows editing lists of complex objects, like a list of Classes + * + * @param config The {@link AbstractConfig} the config screen is for + * @param list The list of objects this screen will be responsible for + * @param field The field this list belongs to in the main config + * @param invoker The object used to invoke the field, when setting the new value + * @param parent The parent {@link Screen} this screen will return to when closed + * @param edited Was this list edited + * @return A fully usable Cloth Config screen + */ + private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + + ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { + Object item = list.get(i); + + int finalI = i; + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.buttons.entry", (i + 1)), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), + button -> { + list.remove(finalI); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + }, + edited + ) + ); + } + + // Add a button to add new list entries + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Class elementType = (Class) paramType.getActualTypeArguments()[0]; + + category.addEntry( + new ClothConfigButtonEntry( + Component.literal(""), + Component.translatable("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); + list.add(newItem); + saveFieldValue(list, field, invoker); + Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true)); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to create new list entry", e); + } + } + ) + ); + } + + builder.setSavingRunnable(() -> safeSaveConfig(config)); + return builder.build(); + } + + /** + * A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields + * + * @param baseConfig The {@link AbstractConfig} to convert + * @param config The base class that is being processed + * @param builder The {@link ClothConfigScreenBuilder} we are currently working with + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + + // We ignore static, transient fields or fields marked with @HideFromScreen + final int fieldModifiers = field.getModifiers(); + if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class)) + continue; + + Object val = field.get(config); + + // Field is marked as sub-config, so we add a button field for it + if (field.isAnnotationPresent(SubConfig.class)) { + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( + Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) + ) + ); + continue; + } + } + + // Boolean Values + if (val instanceof Boolean bool) { + configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(bool).build()); + } + + // Enum Values + if (val instanceof Enum enumValue) { + Class enumClass = (Class)enumValue.getDeclaringClass(); + configCategory.addEntry(entryBuilder.startEnumSelector( + getTranslationKey(baseConfig, config, field.getName()), + enumClass, + enumValue) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(enumValue) + .build()); + } + + // Int Values + if (val instanceof Integer intt) { + configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(intt).build()); + } + + // Long Values + if (val instanceof Long longt) { + configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(longt).build()); + } + + // Float Values + if (val instanceof Float floatt) { + configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(floatt).build()); + } + + // Double Values + if (val instanceof Double doublet) { + configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(doublet).build()); + } + + // String Values + if (val instanceof String string) { + configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(newValue, field, config)) + .setDefaultValue(string).build()); + } + + // Lists + if (val instanceof List list) { + Type listType = field.getGenericType(); + if (listType instanceof ParameterizedType paramType) { + Type elementType = paramType.getActualTypeArguments()[0]; + + // String List + if (elementType.equals(String.class)) { + configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Int List + } else if (elementType.equals(Integer.class)) { + configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Long List + } else if (elementType.equals(Long.class)) { + configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Float List + } else if (elementType.equals(Float.class)) { + configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + + // Double List + } else if (elementType.equals(Double.class)) { + configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List) list) + .setTooltip(getToolTip(field)) + .setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config)) + .setDefaultValue((List) list).build()); + } else { + // List of complex objects + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), + Component.translatable("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) + ) + ); + } + } + } + + } catch (Exception e) { + CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e); + } + } + } + + /** + * Helper method to supply tooltips to config fields. + * If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation + * or generate one from the field name + * + * @param field The field that is being processed + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { + Component component = Component.empty(); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); + component = Component.literal(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); + Component c = Component.empty(); + + for (String comment : tooltip.value()) { + c.getSiblings().add(Component.literal(comment)); + } + + component = c; + } + + return component; + } + + /** + * A helper method to build translation keys for config screens, fields etc + * + * @param baseConfig The {@link AbstractConfig} being processed + * @param currentConfig The field being processed + * @param fieldName The raw name of the field + * @return A {@link Component} with the new translation key + */ + private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) { + String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase(); + + if (currentConfig != baseConfig) { + baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase(); + } + + if (fieldName != null) { + baseKey += "." + fieldName.toLowerCase(); + } + + return Component.translatable(baseKey); + } + + /** + * Helper method to write changes values back to the config using reflection + * + * @param value The new value of the field + * @param field The field that needs to be updated + * @param config The object used to invoke the field for updating + */ + private static void saveFieldValue(Object value, Field field, Object config) { + try { + if (value instanceof List && !field.getType().equals(List.class)) { + List newList = (List)field.getType().getDeclaredConstructor().newInstance(); + newList.addAll((List)value); + field.set(config, newList); + } else { + field.set(config, value); + } + } catch (Exception e) { + CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e); + } + } + + /** + * Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui + * can generate invalid values, that cause the config saving to fail, and then save an empty file. + * This method makes a backup of the config before writing to it, and restores the backup if it fails + * + * @param config The {@link AbstractConfig} being saved + */ + private static void safeSaveConfig(AbstractConfig config) { + File configPath = config.getConfigPath(); + Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak"); + + try { + Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING); + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { + Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastId.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { + Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + config.configReloaded(); + } catch (Exception restoreError) { + CraterConstants.LOG.error("Failed to restore config backup", restoreError); + } + } + } + +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java index 4072ea9..4eca79c 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class CraterConfigScreen extends Screen { public static final float SCROLLBAR_BOTTOM_COLOR = .5f; public static final float SCROLLBAR_TOP_COLOR = .67f; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java index ed34f6b..c46ea5d 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class AbstractConfigWidget extends BaseWidget { public static final int buttonWidth = 200; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java index 0583e61..088d2a5 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class BaseWidget extends Option { public static final int resetButtonOffset = 48; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java new file mode 100644 index 0000000..1e4db60 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java @@ -0,0 +1,128 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.mojang.blaze3d.platform.Window; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author HypherionSA + * A Custom Cloth Config GUI entry to allow buttons to be added to the GUI + */ +public class ClothConfigButtonEntry extends AbstractConfigListEntry { + + private final Button button; + private final Button deleteButton; + private final Component displayName; + private final boolean hasDeleteButton; + private final boolean wasEdited; + + /** + * Create a new Cloth Button Entry, that will have no delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) { + this(displayName, fieldName, onPress, null, false); + } + + /*** + * Create a new Cloth Button Entry, with optional delete button + * + * @param displayName The Display Name that will be used for the field + * @param fieldName The Display Name that will be used on the button + * @param onPress The Action to perform when the button was pressed + * @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled + * @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button + */ + public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) { + super(fieldName, false); + this.hasDeleteButton = deletePress != null; + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; + this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); + this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; + this.displayName = displayName; + } + + @Override + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { + matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); + } else { + this.button.setX(x + entryWidth - this.button.getWidth()); + } + } + + button.setY(y + (entryHeight - 20) / 2); + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { + deleteButton.setY(y + (entryHeight - 20) / 2); + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } + + @Override + public Void getValue() { return null; } + + @Override + public Optional getDefaultValue() { return Optional.empty(); } + + @Override + public void save() {} + + @NotNull + @Override + public List children() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public List narratables() { + ArrayList children = new ArrayList<>(); + children.add(button); + + if (hasDeleteButton) { + children.add(deleteButton); + } + + return children; + } + + @Override + public boolean isEdited() { + return wasEdited; + } +} \ No newline at end of file diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java index 2478c7d..d93386f 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class InternalConfigButton extends AbstractButton { CraterConfigScreen screen; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java index f49ca68..3ac9f32 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public abstract class Option extends AbstractContainerEventHandler { public Component text; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java index 056f32c..f97b5bc 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java index cc05cec..ef5f955 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class TextConfigOption extends AbstractConfigWidget { private final Function toString; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java index 03ea11d..c90b67d 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -10,6 +10,7 @@ import java.util.function.Function; * Copied from Cloth Config Lite * ... */ +@Deprecated(forRemoval = true, since = "2.1.3") public class ToggleButton extends AbstractConfigWidget { private final List options; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java index 42c0c0c..8c8282d 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; /** * @author HypherionSA */ +@Deprecated(forRemoval = true, since = "2.1.3") public class WrappedEditBox extends EditBox { public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java new file mode 100644 index 0000000..bec46eb --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/ClothScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allow mods to make use of the new Cloth Config based Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ClothScreen { +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java index c4bcc23..716b4c9 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -1,10 +1,7 @@ package com.hypherionmc.craterlib.nojang.client.gui; import lombok.RequiredArgsConstructor; -import net.minecraft.client.gui.screens.LevelLoadingScreen; -import net.minecraft.client.gui.screens.ReceivingLevelScreen; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.*; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.realms.RealmsScreen; @@ -29,6 +26,14 @@ public class BridgedScreen { return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; } + public boolean isPauseScreen() { + return internal instanceof PauseScreen; + } + + public boolean isDisconnetedScreen() { + return internal instanceof DisconnectedScreen; + } + public Screen toMojang() { return internal; } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java index 2241836..9bf7600 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,34 @@ public class BridgedPlayer { return BridgedBlockPos.of(internal.getOnPos()); } + public float getHealth() { + return internal.getHealth(); + } + + public float getMaxHealth() { + return internal.getMaxHealth(); + } + + public String getHeldItemMainHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString(); + } + + return value; + } + + public String getHeldItemOffHand() { + String value = "Nothing"; + + if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) { + value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString(); + } + + return value; + } + @Nullable public ServerGamePacketListenerImpl getConnection() { if (isServerPlayer()) { diff --git a/1.21/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.21/Common/src/main/resources/assets/craterlib/lang/en_us.json index 03115d9..7dbfff1 100644 --- a/1.21/Common/src/main/resources/assets/craterlib/lang/en_us.json +++ b/1.21/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -4,5 +4,9 @@ "t.clc.cancel_discard": "Discard", "t.clc.quit_config": "Unsaved Changes", "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", - "t.clc.quit_discard": "Quit & Discard" + "t.clc.quit_discard": "Quit & Discard", + + "cl.buttons.edit": "Edit", + "cl.buttons.add_entry": "+ Add Entry", + "cl.buttons.entry": "Entry %s" } diff --git a/1.21/Fabric/build.gradle b/1.21/Fabric/build.gradle index b8ced84..37b2908 100644 --- a/1.21/Fabric/build.gradle +++ b/1.21/Fabric/build.gradle @@ -12,6 +12,7 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" @@ -125,9 +126,11 @@ publisher { modrinthDepends { required("fabric-api") + optional("cloth-config", "modmenu") } curseDepends { required("fabric-api") + optional("cloth-config", "modmenu") } } diff --git a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java index 5b34ff1..b4f4429 100644 --- a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java +++ b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -1,8 +1,12 @@ package com.hypherionmc.craterlib; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi { Map> configScreens = new HashMap<>(); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)); + } else { + //configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen)); } }); diff --git a/1.21/NeoForge/build.gradle b/1.21/NeoForge/build.gradle index 4339fd1..cdc92d8 100644 --- a/1.21/NeoForge/build.gradle +++ b/1.21/NeoForge/build.gradle @@ -7,6 +7,8 @@ dependencies { stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") + modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") + // Do not edit or remove implementation project(":Common") } @@ -115,4 +117,12 @@ publisher { setArtifact(remapJar) setCurseEnvironment("both") setIsManualRelease(true) + + curseDepends { + optional("cloth-config") + } + + modrinthDepends { + optional("cloth-config") + } } diff --git a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java index e7d9013..eacd3a3 100644 --- a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java @@ -1,12 +1,15 @@ package com.hypherionmc.craterlib.client; import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; +import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.hypherionmc.craterlib.core.event.CraterEventBus; import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; import com.hypherionmc.craterlib.nojang.client.BridgedOptions; import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -52,9 +55,14 @@ public class NeoForgeClientHelper implements ClientPlatform { CraterEventBus.INSTANCE.postEvent(event); ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + + if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { + ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); + } else { + //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); } }); } diff --git a/1.21/changelog.md b/1.21/changelog.md index 09222a4..c4a47e5 100644 --- a/1.21/changelog.md +++ b/1.21/changelog.md @@ -1,12 +1,9 @@ -**New Features**: - -- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper) -- Added APIs for working with FTB Ranks and LuckPerms groups - **Bug Fixes**: -- Fixed Vanish compact API being swapped +- Added a workaround for LuckPerms turning players into ghost players +- Fixed Missing Getters on LuckPerms events -**Changes**: +**New Features**: -- Config library now logs which line of the config the error is on \ No newline at end of file +- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs +- Added new APIs for Simple RPC (V4 rewrite) \ No newline at end of file diff --git a/1.21/gradle.properties b/1.21/gradle.properties index 1a7e91c..fb932bf 100644 --- a/1.21/gradle.properties +++ b/1.21/gradle.properties @@ -1,8 +1,8 @@ #Project version_major=2 version_minor=1 -version_patch=2 -version_build=1 +version_patch=3 +version_build=0 #Mod mod_author=HypherionSA @@ -29,6 +29,7 @@ lombok=1.18.32 adventure=4.17.0 rpc_sdk=1.0 discord_formatter=2.0.0 +cloth_config=15.0.140 # Mod Dependencies fabrictailor=2.3.1 diff --git a/commit.sha b/commit.sha index 9bd4420..e59c320 100644 --- a/commit.sha +++ b/commit.sha @@ -1 +1 @@ -5a8769bee997f9cc024bdda619289be22b9465c0 \ No newline at end of file +5d0ad68c64a0ce008c9169967ba0cb9c10c960b8 \ No newline at end of file diff --git a/patches/1.18.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.18.2/.jenkins/Jenkinsfile.snapshot.patch index 9fb8116..67331a5 100644 --- a/patches/1.18.2/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.18.2/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.18.2"; diff --git a/patches/1.18.2/Common/build.gradle.patch b/patches/1.18.2/Common/build.gradle.patch index b2dbdc3..84bd5a5 100644 --- a/patches/1.18.2/Common/build.gradle.patch +++ b/patches/1.18.2/Common/build.gradle.patch @@ -1,11 +1,13 @@ --- a/Common/build.gradle +++ b/Common/build.gradle -@@ -1,8 +1,8 @@ +@@ -1,10 +1,10 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" dependencies { - stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") + + stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}") + } diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch index 2149294..916d041 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java -@@ -14,6 +14,7 @@ +@@ -15,6 +15,7 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; @@ -8,7 +8,7 @@ import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.ApiStatus; -@@ -53,7 +54,7 @@ +@@ -54,7 +55,7 @@ public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) .executes(context -> executor.run( @@ -17,7 +17,7 @@ GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), BridgedCommandSourceStack.of(context.getSource())) )); -@@ -63,7 +64,7 @@ +@@ -64,7 +65,7 @@ public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) .executes(context -> executor.run( @@ -26,7 +26,7 @@ BoolArgumentType.getBool(context, key), BridgedCommandSourceStack.of(context.getSource()) ))); -@@ -73,7 +74,7 @@ +@@ -74,7 +75,7 @@ public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) .executes(context -> executor.run( @@ -35,7 +35,7 @@ StringArgumentType.getString(context, key), BridgedCommandSourceStack.of(context.getSource()) ))); -@@ -83,7 +84,7 @@ +@@ -84,7 +85,7 @@ public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) .executes(context -> executor.run( @@ -44,7 +44,7 @@ StringArgumentType.getString(context, key), BridgedCommandSourceStack.of(context.getSource()) ))); -@@ -93,7 +94,7 @@ +@@ -94,7 +95,7 @@ public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) .executes(context -> executor.run( @@ -53,7 +53,7 @@ StringArgumentType.getString(context, key), BridgedCommandSourceStack.of(context.getSource()) ))); -@@ -103,7 +104,7 @@ +@@ -104,7 +105,7 @@ public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) .executes(context -> executor.run( @@ -62,15 +62,19 @@ IntegerArgumentType.getInteger(context, key), BridgedCommandSourceStack.of(context.getSource()) ))); -@@ -137,10 +138,10 @@ - } +@@ -139,14 +140,14 @@ private boolean checkPermission(CommandSourceStack stack) { -- if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + try { +- if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty()) + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !(stack.getEntity() instanceof Player) || luckPermNode.isEmpty()) - return stack.hasPermission(this.permLevel); + return stack.hasPermission(this.permLevel); -- return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); +- return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to check luckperms permissions", e); + return stack.hasPermission(this.permLevel); + } + return LuckPermsCompat.INSTANCE.hasPermission((ServerPlayer) stack.getEntity(), this.luckPermNode) || stack.hasPermission(this.permLevel); } diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..d534c13 --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,120 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -14,6 +14,8 @@ + import net.minecraft.client.gui.components.toasts.SystemToast; + import net.minecraft.client.gui.screens.Screen; + import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.network.chat.TranslatableComponent; + import org.jetbrains.annotations.ApiStatus; + import org.jetbrains.annotations.Nullable; + +@@ -89,7 +91,7 @@ + .setParentScreen(parent) + .setTitle(getTranslationKey(config, invoker, field.getName())); + +- ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries")); ++ ConfigCategory category = builder.getOrCreateCategory(new TextComponent("Entries")); + + // Handle Existing items in the list + for (int i = 0; i < list.size(); i++) { +@@ -99,8 +101,8 @@ + // Add a button to open the edit screen, as well as a delete button + category.addEntry( + new ClothConfigButtonEntry( +- Component.translatable("cl.buttons.entry", (i + 1)), +- Component.translatable("cl.buttons.edit"), ++ new TranslatableComponent("cl.buttons.entry", (i + 1)), ++ new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(config, item, builder.build()) + ), +@@ -121,8 +123,8 @@ + + category.addEntry( + new ClothConfigButtonEntry( +- Component.literal(""), +- Component.translatable("cl.buttons.add_entry"), ++ new TextComponent(""), ++ new TranslatableComponent("cl.buttons.add_entry"), + button -> { + try { + Object newItem = elementType.getDeclaredConstructor().newInstance(); +@@ -150,7 +152,7 @@ + */ + private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) { + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); +- ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General")); ++ ConfigCategory configCategory = builder.getOrCreateCategory(new TextComponent("General")); + + for (Field field : config.getClass().getDeclaredFields()) { + try { +@@ -168,8 +170,8 @@ + if (val != null) { + configCategory.addEntry( + new ClothConfigButtonEntry( +- Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), +- Component.translatable("cl.buttons.edit"), ++ new TranslatableComponent("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()), ++ new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildSubScreen(baseConfig, val, builder.build()) + ) +@@ -285,7 +287,7 @@ + configCategory.addEntry( + new ClothConfigButtonEntry( + getTranslationKey(baseConfig, config, field.getName()), +- Component.translatable("cl.buttons.edit"), ++ new TranslatableComponent("cl.buttons.edit"), + button -> Minecraft.getInstance().setScreen( + buildListScreen(baseConfig, list, field, config, builder.build(), false) + ) +@@ -310,19 +312,19 @@ + * @return A {@link Component} that can be used for the tooltip + */ + private static Component getToolTip(Field field) { +- Component component = Component.empty(); ++ Component component = new TextComponent(""); + + if (field.isAnnotationPresent(SpecComment.class)) { + SpecComment comment = field.getAnnotation(SpecComment.class); +- component = Component.literal(comment.value()); ++ component = new TextComponent(comment.value()); + } + + if (field.isAnnotationPresent(Tooltip.class)) { + Tooltip tooltip = field.getAnnotation(Tooltip.class); +- Component c = Component.empty(); ++ Component c = new TextComponent(""); + + for (String comment : tooltip.value()) { +- c.getSiblings().add(Component.literal(comment)); ++ c.getSiblings().add(new TextComponent(comment)); + } + + component = c; +@@ -350,7 +352,7 @@ + baseKey += "." + fieldName.toLowerCase(); + } + +- return Component.translatable(baseKey); ++ return new TranslatableComponent(baseKey); + } + + /** +@@ -390,11 +392,11 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( +- SystemToast.SystemToastId.PACK_LOAD_FAILURE, +- Component.literal("Failed To Save Config"), +- Component.literal("Restoring Backup Copy. Check log for details")) ++ SystemToast.SystemToastIds.PACK_LOAD_FAILURE, ++ new TextComponent("Failed To Save Config"), ++ new TextComponent("Restoring Backup Copy. Check log for details")) + ); + CraterConstants.LOG.error("Failed to save config, restoring backup", e); + try { diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index 2cd013d..173da1d 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -23,7 +23,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -@@ -49,7 +50,7 @@ +@@ -50,7 +51,7 @@ private boolean dragging; public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { @@ -32,7 +32,7 @@ this.parent = parent; this.config = config; if (subConfig != null) { -@@ -64,11 +65,11 @@ +@@ -65,11 +66,11 @@ } private static Component toText(Enum val) { @@ -46,7 +46,7 @@ } private void setupScreenFromConfig(Object object, Class clazz) { -@@ -94,7 +95,7 @@ +@@ -95,7 +96,7 @@ tooltipLang = field.getAnnotation(Tooltip.class).value(); } @@ -55,7 +55,7 @@ val, () -> val, (ret) -> { -@@ -158,7 +159,7 @@ +@@ -159,7 +160,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -64,7 +64,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -172,24 +173,24 @@ +@@ -173,24 +174,24 @@ ((List) children()).addAll(options); int buttonWidths = Math.min(200, (width - 50 - 12) / 3); @@ -101,7 +101,7 @@ int y = (int) (TOP + 4 - Math.round(scrollerAmount)); for (Option option : options) { -@@ -216,23 +217,24 @@ +@@ -217,23 +218,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -143,7 +143,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +242,22 @@ +@@ -241,21 +243,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -177,7 +177,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +268,16 @@ +@@ -266,15 +269,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); @@ -202,7 +202,7 @@ } public int scrollHeight() { -@@ -321,22 +325,22 @@ +@@ -322,22 +326,22 @@ @Override public void onClose() { if (isEdited()) { @@ -231,7 +231,7 @@ } @Override -@@ -375,15 +379,15 @@ +@@ -376,15 +380,15 @@ } } diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch index f086ca2..a5b7ec1 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch @@ -10,7 +10,7 @@ import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.EditBox; -@@ -17,11 +17,11 @@ +@@ -18,11 +18,11 @@ public W widget; @Override diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch index b5e7910..74c4f26 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch @@ -16,7 +16,7 @@ /** * Copied from Cloth Config Lite -@@ -16,7 +16,7 @@ +@@ -17,7 +17,7 @@ public class BaseWidget extends Option { public static final int resetButtonOffset = 48; @@ -25,7 +25,7 @@ private boolean hideReset = false; private boolean isSubConfig = false; -@@ -39,8 +39,8 @@ +@@ -40,8 +40,8 @@ } @Override @@ -36,7 +36,7 @@ boolean edited = isEdited() || hasErrors; if (edited) { text.withStyle(ChatFormatting.ITALIC); -@@ -50,9 +50,9 @@ +@@ -51,9 +51,9 @@ } else { text.withStyle(ChatFormatting.GRAY); } diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch new file mode 100644 index 0000000..f2e8ba0 --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch @@ -0,0 +1,67 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java +@@ -1,13 +1,14 @@ + package com.hypherionmc.craterlib.client.gui.config.widgets; + + import com.mojang.blaze3d.platform.Window; ++import com.mojang.blaze3d.vertex.PoseStack; + import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; + import net.minecraft.client.Minecraft; +-import net.minecraft.client.gui.GuiGraphics; + import net.minecraft.client.gui.components.Button; + import net.minecraft.client.gui.components.events.GuiEventListener; + import net.minecraft.client.gui.narration.NarratableEntry; + import net.minecraft.network.chat.Component; ++import net.minecraft.network.chat.TextComponent; + import org.jetbrains.annotations.NotNull; + import org.jetbrains.annotations.Nullable; + +@@ -53,36 +54,36 @@ + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; +- this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); +- this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; ++ this.button = new Button(0, 0, mainButtonWidth, 20, fieldName, onPress); ++ this.deleteButton = deletePress != null ? new Button(0, 0, 20, 20, new TextComponent("X"), deletePress) : null; + this.displayName = displayName; + } + + @Override +- public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { ++ public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); +- this.button.setX(x); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); ++ this.button.x = x; + if (hasDeleteButton) { +- this.deleteButton.setX(x + this.button.getWidth() + 4); ++ this.deleteButton.x = x + this.button.getWidth() + 4; + } + } else { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { +- this.button.setX(x + entryWidth - this.button.getWidth() - 24); +- this.deleteButton.setX(x + entryWidth - 20); ++ this.button.x = x + entryWidth - this.button.getWidth() - 24; ++ this.deleteButton.x = x + entryWidth - 20; + } else { +- this.button.setX(x + entryWidth - this.button.getWidth()); ++ this.button.x = x + entryWidth - this.button.getWidth(); + } + } + +- button.setY(y + (entryHeight - 20) / 2); ++ button.y = y + (entryHeight - 20) / 2; + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { +- deleteButton.setY(y + (entryHeight - 20) / 2); ++ deleteButton.y = y + (entryHeight - 20) / 2; + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch index 892c52e..153e912 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch @@ -15,7 +15,7 @@ /** * @author HypherionSA -@@ -22,19 +24,19 @@ +@@ -23,19 +25,19 @@ } @Override diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch index 8e46a48..e9fca27 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch @@ -12,7 +12,7 @@ import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.network.chat.Component; -@@ -34,7 +34,7 @@ +@@ -35,7 +35,7 @@ @Getter private List langKeys = new ArrayList<>(); diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch index 01902bf..37e952d 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch @@ -15,7 +15,7 @@ /** * @author HypherionSA -@@ -23,12 +23,12 @@ +@@ -24,12 +24,12 @@ this.subConfig = subConfig; this.screen = screen; diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch index 7107ad2..3aac8bb 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch @@ -10,7 +10,7 @@ import java.util.function.Function; -@@ -29,7 +29,7 @@ +@@ -30,7 +30,7 @@ } @Override diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch index ff1911a..2afde0e 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch @@ -8,7 +8,7 @@ import java.util.List; import java.util.function.Function; -@@ -18,7 +19,7 @@ +@@ -19,7 +20,7 @@ public ToggleButton(List options, Function toComponent) { this.options = options; this.toComponent = toComponent; diff --git a/patches/1.18.2/Fabric/build.gradle.patch b/patches/1.18.2/Fabric/build.gradle.patch index 3a928b6..b6f6723 100644 --- a/patches/1.18.2/Fabric/build.gradle.patch +++ b/patches/1.18.2/Fabric/build.gradle.patch @@ -7,15 +7,15 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") - + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" - -@@ -116,8 +113,8 @@ +@@ -117,8 +114,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.18.2] CraterLib - ${project.version}") + setGameVersions("1.18.2") setLoaders("fabric", "quilt") diff --git a/patches/1.18.2/Forge/build.gradle.patch b/patches/1.18.2/Forge/build.gradle.patch index cd9e4f0..efde3e9 100644 --- a/patches/1.18.2/Forge/build.gradle.patch +++ b/patches/1.18.2/Forge/build.gradle.patch @@ -7,9 +7,9 @@ - // NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}") + modImplementation("maven.modrinth:vanishmod:${vanishmod}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +107,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +109,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index 1f38ae1..071ca42 100644 --- a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,13 +1,16 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,12 +1,12 @@ +@@ -1,12 +1,15 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -import net.minecraftforge.client.ConfigScreenHandler; @@ -15,7 +18,7 @@ import net.minecraftforge.forgespi.language.IModInfo; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -@@ -19,18 +19,18 @@ +@@ -19,24 +22,30 @@ /** * @author HypherionSA */ @@ -32,9 +35,24 @@ private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } diff --git a/patches/1.18.2/NeoForge/build.gradle.patch b/patches/1.18.2/NeoForge/build.gradle.patch index 921f670..39e2d95 100644 --- a/patches/1.18.2/NeoForge/build.gradle.patch +++ b/patches/1.18.2/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ /dev/null -@@ -1,123 +1,0 @@ +@@ -1,133 +1,0 @@ -archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" - -dependencies { @@ -10,6 +10,8 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") - +- modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -117,10 +119,18 @@ - setVersionType("release") - setChangelog(rootProject.file("changelog.md")) - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index 6f63513..ac70abe 100644 --- a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ /dev/null -@@ -1,61 +1,0 @@ +@@ -1,69 +1,0 @@ -package com.hypherionmc.craterlib.client; - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; +-import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; -import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; -import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -55,9 +58,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.18.2/gradle.properties.patch b/patches/1.18.2/gradle.properties.patch index 7d86d47..c147293 100644 --- a/patches/1.18.2/gradle.properties.patch +++ b/patches/1.18.2/gradle.properties.patch @@ -23,8 +23,12 @@ # Dependencies moon_config=1.0.10 -@@ -31,18 +28,17 @@ +@@ -29,21 +26,20 @@ + adventure=4.17.0 + rpc_sdk=1.0 discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=6.5.102 # Mod Dependencies -fabrictailor=2.3.1 diff --git a/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch index 01e605a..f8aa05b 100644 --- a/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.19.2"; diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..a08ded3 --- /dev/null +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,14 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,9 +390,9 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( +- SystemToast.SystemToastId.PACK_LOAD_FAILURE, ++ SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index 88b3741..a1d82df 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -20,7 +20,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -@@ -158,7 +157,7 @@ +@@ -159,7 +158,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -29,7 +29,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -177,19 +176,19 @@ +@@ -178,19 +177,19 @@ } @Override @@ -59,7 +59,7 @@ int y = (int) (TOP + 4 - Math.round(scrollerAmount)); for (Option option : options) { -@@ -216,23 +215,24 @@ +@@ -217,23 +216,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -101,7 +101,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +240,22 @@ +@@ -241,21 +241,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -135,7 +135,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +266,16 @@ +@@ -266,15 +267,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); @@ -160,7 +160,7 @@ } public int scrollHeight() { -@@ -331,12 +333,12 @@ +@@ -332,12 +334,12 @@ } @Override @@ -175,7 +175,7 @@ } @Override -@@ -375,7 +377,7 @@ +@@ -376,7 +378,7 @@ } } @@ -184,7 +184,7 @@ if (mouseX > startX && mouseX < startX + sizeX) { if (mouseY > startY && mouseY < startY + sizeY) { List list = new ArrayList<>(); -@@ -383,7 +385,7 @@ +@@ -384,7 +386,7 @@ for (String desc : description) { list.add(Component.translatable(desc)); } diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch index f086ca2..a5b7ec1 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch @@ -10,7 +10,7 @@ import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.EditBox; -@@ -17,11 +17,11 @@ +@@ -18,11 +18,11 @@ public W widget; @Override diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch index 34476cd..6764bee 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch @@ -11,7 +11,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -@@ -16,7 +16,7 @@ +@@ -17,7 +17,7 @@ public class BaseWidget extends Option { public static final int resetButtonOffset = 48; @@ -20,7 +20,7 @@ private boolean hideReset = false; private boolean isSubConfig = false; -@@ -39,7 +39,7 @@ +@@ -40,7 +40,7 @@ } @Override @@ -29,7 +29,7 @@ MutableComponent text = Component.literal(this.text.getString()); boolean edited = isEdited() || hasErrors; if (edited) { -@@ -50,9 +50,9 @@ +@@ -51,9 +51,9 @@ } else { text.withStyle(ChatFormatting.GRAY); } diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch new file mode 100644 index 0000000..3ef4853 --- /dev/null +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch @@ -0,0 +1,62 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java +@@ -1,9 +1,9 @@ + package com.hypherionmc.craterlib.client.gui.config.widgets; + + import com.mojang.blaze3d.platform.Window; ++import com.mojang.blaze3d.vertex.PoseStack; + import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; + import net.minecraft.client.Minecraft; +-import net.minecraft.client.gui.GuiGraphics; + import net.minecraft.client.gui.components.Button; + import net.minecraft.client.gui.components.events.GuiEventListener; + import net.minecraft.client.gui.narration.NarratableEntry; +@@ -53,36 +53,36 @@ + this.wasEdited = wasEdited; + + int mainButtonWidth = hasDeleteButton ? 75 : 100; +- this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build(); +- this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null; ++ this.button = new Button(0, 0, mainButtonWidth, 20, fieldName, onPress); ++ this.deleteButton = deletePress != null ? new Button(0, 0, 20, 20, Component.literal("X"), deletePress) : null; + this.displayName = displayName; + } + + @Override +- public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { ++ public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); +- this.button.setX(x); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); ++ this.button.x = x; + if (hasDeleteButton) { +- this.deleteButton.setX(x + this.button.getWidth() + 4); ++ this.deleteButton.x = x + this.button.getWidth() + 4; + } + } else { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { +- this.button.setX(x + entryWidth - this.button.getWidth() - 24); +- this.deleteButton.setX(x + entryWidth - 20); ++ this.button.x = x + entryWidth - this.button.getWidth() - 24; ++ this.deleteButton.x = x + entryWidth - 20; + } else { +- this.button.setX(x + entryWidth - this.button.getWidth()); ++ this.button.x = x + entryWidth - this.button.getWidth(); + } + } + +- button.setY(y + (entryHeight - 20) / 2); ++ button.y = y + (entryHeight - 20) / 2; + button.render(matrices, mouseX, mouseY, delta); + + if (hasDeleteButton) { +- deleteButton.setY(y + (entryHeight - 20) / 2); ++ deleteButton.y = y + (entryHeight - 20) / 2; + deleteButton.render(matrices, mouseX, mouseY, delta); + } + } diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch index 45315ec..cd79789 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch @@ -14,7 +14,7 @@ /** * @author HypherionSA -@@ -22,7 +23,7 @@ +@@ -23,7 +24,7 @@ } @Override @@ -23,7 +23,7 @@ if (cancel) { setMessage(Component.translatable(screen.isEdited() ? "t.clc.cancel_discard" : "gui.cancel")); } else { -@@ -30,11 +31,11 @@ +@@ -31,11 +32,11 @@ active = screen.isEdited() && !hasErrors; setMessage(Component.translatable(hasErrors ? "t.clc.error" : "t.clc.save")); } diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch index 8e46a48..e9fca27 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch @@ -12,7 +12,7 @@ import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.network.chat.Component; -@@ -34,7 +34,7 @@ +@@ -35,7 +35,7 @@ @Getter private List langKeys = new ArrayList<>(); diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch index 4863fd7..7989713 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch @@ -11,7 +11,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -@@ -23,11 +23,11 @@ +@@ -24,11 +24,11 @@ this.subConfig = subConfig; this.screen = screen; diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch index 7107ad2..3aac8bb 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch @@ -10,7 +10,7 @@ import java.util.function.Function; -@@ -29,7 +29,7 @@ +@@ -30,7 +30,7 @@ } @Override diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch index 112672b..612513f 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java -@@ -18,7 +18,7 @@ +@@ -19,7 +19,7 @@ public ToggleButton(List options, Function toComponent) { this.options = options; this.toComponent = toComponent; diff --git a/patches/1.19.2/Fabric/build.gradle.patch b/patches/1.19.2/Fabric/build.gradle.patch index 1cbe39f..555bcee 100644 --- a/patches/1.19.2/Fabric/build.gradle.patch +++ b/patches/1.19.2/Fabric/build.gradle.patch @@ -5,15 +5,15 @@ stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") - + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" - -@@ -116,8 +115,8 @@ +@@ -117,8 +116,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.19.2] CraterLib - ${project.version}") + setGameVersions("1.19.2") setLoaders("fabric", "quilt") diff --git a/patches/1.19.2/Forge/build.gradle.patch b/patches/1.19.2/Forge/build.gradle.patch index df5402e..6d521bd 100644 --- a/patches/1.19.2/Forge/build.gradle.patch +++ b/patches/1.19.2/Forge/build.gradle.patch @@ -10,9 +10,9 @@ + stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") + stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +110,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +112,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index b5243f2..38aee7f 100644 --- a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,24 +1,43 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,8 +1,8 @@ +@@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -@@ -28,9 +28,9 @@ + import net.minecraftforge.client.ConfigScreenHandler; +@@ -28,15 +31,21 @@ */ @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } diff --git a/patches/1.19.2/NeoForge/build.gradle.patch b/patches/1.19.2/NeoForge/build.gradle.patch index 921f670..39e2d95 100644 --- a/patches/1.19.2/NeoForge/build.gradle.patch +++ b/patches/1.19.2/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ /dev/null -@@ -1,123 +1,0 @@ +@@ -1,133 +1,0 @@ -archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" - -dependencies { @@ -10,6 +10,8 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") - +- modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -117,10 +119,18 @@ - setVersionType("release") - setChangelog(rootProject.file("changelog.md")) - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index 6f63513..ac70abe 100644 --- a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ /dev/null -@@ -1,61 +1,0 @@ +@@ -1,69 +1,0 @@ -package com.hypherionmc.craterlib.client; - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; +-import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; -import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; -import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -55,9 +58,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.19.2/gradle.properties.patch b/patches/1.19.2/gradle.properties.patch index 45c1fa0..75fd708 100644 --- a/patches/1.19.2/gradle.properties.patch +++ b/patches/1.19.2/gradle.properties.patch @@ -23,8 +23,12 @@ # Dependencies moon_config=1.0.10 -@@ -31,18 +28,17 @@ +@@ -29,21 +26,20 @@ + adventure=4.17.0 + rpc_sdk=1.0 discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=8.3.134 # Mod Dependencies -fabrictailor=2.3.1 diff --git a/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch index b893e15..37c5533 100644 --- a/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.19.3"; diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..a08ded3 --- /dev/null +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,14 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,9 +390,9 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( +- SystemToast.SystemToastId.PACK_LOAD_FAILURE, ++ SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index a70df59..6a88d1a 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -8,7 +8,7 @@ import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.GameRenderer; -@@ -158,7 +157,7 @@ +@@ -159,7 +158,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -17,7 +17,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -177,19 +176,19 @@ +@@ -178,19 +177,19 @@ } @Override @@ -47,7 +47,7 @@ int y = (int) (TOP + 4 - Math.round(scrollerAmount)); for (Option option : options) { -@@ -216,23 +215,24 @@ +@@ -217,23 +216,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -89,7 +89,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +240,22 @@ +@@ -241,21 +241,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -123,7 +123,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +266,16 @@ +@@ -266,15 +267,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); @@ -148,7 +148,7 @@ } public int scrollHeight() { -@@ -331,12 +333,12 @@ +@@ -332,12 +334,12 @@ } @Override @@ -163,7 +163,7 @@ } @Override -@@ -375,7 +377,7 @@ +@@ -376,7 +378,7 @@ } } @@ -172,7 +172,7 @@ if (mouseX > startX && mouseX < startX + sizeX) { if (mouseY > startY && mouseY < startY + sizeY) { List list = new ArrayList<>(); -@@ -383,7 +385,7 @@ +@@ -384,7 +386,7 @@ for (String desc : description) { list.add(Component.translatable(desc)); } diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch index 3e2023e..833b729 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java.patch @@ -10,7 +10,7 @@ import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.EditBox; -@@ -17,7 +17,7 @@ +@@ -18,7 +18,7 @@ public W widget; @Override diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch index da073f3..3719bbd 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java.patch @@ -11,7 +11,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -@@ -39,7 +39,7 @@ +@@ -40,7 +40,7 @@ } @Override @@ -20,7 +20,7 @@ MutableComponent text = Component.literal(this.text.getString()); boolean edited = isEdited() || hasErrors; if (edited) { -@@ -50,7 +50,7 @@ +@@ -51,7 +51,7 @@ } else { text.withStyle(ChatFormatting.GRAY); } diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch new file mode 100644 index 0000000..e35ec1b --- /dev/null +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java.patch @@ -0,0 +1,34 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ClothConfigButtonEntry.java +@@ -1,9 +1,9 @@ + package com.hypherionmc.craterlib.client.gui.config.widgets; + + import com.mojang.blaze3d.platform.Window; ++import com.mojang.blaze3d.vertex.PoseStack; + import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; + import net.minecraft.client.Minecraft; +-import net.minecraft.client.gui.GuiGraphics; + import net.minecraft.client.gui.components.Button; + import net.minecraft.client.gui.components.events.GuiEventListener; + import net.minecraft.client.gui.narration.NarratableEntry; +@@ -59,17 +59,17 @@ + } + + @Override +- public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { ++ public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) { + Window window = Minecraft.getInstance().getWindow(); + Component displayedFieldName = displayName; + if (Minecraft.getInstance().font.isBidirectional()) { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215); + this.button.setX(x); + if (hasDeleteButton) { + this.deleteButton.setX(x + this.button.getWidth() + 4); + } + } else { +- matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); ++ drawString(matrices, Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor()); + if (hasDeleteButton) { + this.button.setX(x + entryWidth - this.button.getWidth() - 24); + this.deleteButton.setX(x + entryWidth - 20); diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch index 85afdd2..a3bd8f9 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch @@ -14,7 +14,7 @@ /** * @author HypherionSA -@@ -22,7 +23,7 @@ +@@ -23,7 +24,7 @@ } @Override @@ -23,7 +23,7 @@ if (cancel) { setMessage(Component.translatable(screen.isEdited() ? "t.clc.cancel_discard" : "gui.cancel")); } else { -@@ -30,7 +31,7 @@ +@@ -31,7 +32,7 @@ active = screen.isEdited() && !hasErrors; setMessage(Component.translatable(hasErrors ? "t.clc.error" : "t.clc.save")); } diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch index 8e46a48..e9fca27 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java.patch @@ -12,7 +12,7 @@ import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.network.chat.Component; -@@ -34,7 +34,7 @@ +@@ -35,7 +35,7 @@ @Getter private List langKeys = new ArrayList<>(); diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch index ebe8f79..08f5acb 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch @@ -11,7 +11,7 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; -@@ -27,7 +27,7 @@ +@@ -28,7 +28,7 @@ } @Override diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch index 7107ad2..3aac8bb 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java.patch @@ -10,7 +10,7 @@ import java.util.function.Function; -@@ -29,7 +29,7 @@ +@@ -30,7 +30,7 @@ } @Override diff --git a/patches/1.19.3/Fabric/build.gradle.patch b/patches/1.19.3/Fabric/build.gradle.patch index 0add004..aa4733d 100644 --- a/patches/1.19.3/Fabric/build.gradle.patch +++ b/patches/1.19.3/Fabric/build.gradle.patch @@ -5,15 +5,15 @@ stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") - + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" - -@@ -116,8 +115,8 @@ +@@ -117,8 +116,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.19.4] CraterLib - ${project.version}") + setGameVersions("1.19.4") setLoaders("fabric", "quilt") diff --git a/patches/1.19.3/Forge/build.gradle.patch b/patches/1.19.3/Forge/build.gradle.patch index 0ff1595..a50952b 100644 --- a/patches/1.19.3/Forge/build.gradle.patch +++ b/patches/1.19.3/Forge/build.gradle.patch @@ -10,9 +10,9 @@ + stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") + stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +110,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +112,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index 5f8832b..a9537a9 100644 --- a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,22 +1,45 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,6 +1,7 @@ +@@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; - import com.hypherionmc.craterlib.core.config.ModuleConfig; +-import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -@@ -28,9 +29,9 @@ ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; + import net.minecraft.client.Minecraft; + import net.minecraft.client.gui.screens.Screen; + import net.minecraftforge.client.ConfigScreenHandler; +@@ -28,17 +31,22 @@ */ @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } +- + } diff --git a/patches/1.19.3/NeoForge/build.gradle.patch b/patches/1.19.3/NeoForge/build.gradle.patch index 921f670..39e2d95 100644 --- a/patches/1.19.3/NeoForge/build.gradle.patch +++ b/patches/1.19.3/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ /dev/null -@@ -1,123 +1,0 @@ +@@ -1,133 +1,0 @@ -archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" - -dependencies { @@ -10,6 +10,8 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") - +- modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -117,10 +119,18 @@ - setVersionType("release") - setChangelog(rootProject.file("changelog.md")) - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index 6f63513..ac70abe 100644 --- a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ /dev/null -@@ -1,61 +1,0 @@ +@@ -1,69 +1,0 @@ -package com.hypherionmc.craterlib.client; - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; +-import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; -import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; -import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -55,9 +58,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.19.3/gradle.properties.patch b/patches/1.19.3/gradle.properties.patch index 25ca512..36fcfc0 100644 --- a/patches/1.19.3/gradle.properties.patch +++ b/patches/1.19.3/gradle.properties.patch @@ -23,8 +23,12 @@ # Dependencies moon_config=1.0.10 -@@ -31,18 +28,17 @@ +@@ -29,21 +26,20 @@ + adventure=4.17.0 + rpc_sdk=1.0 discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=9.1.104 # Mod Dependencies -fabrictailor=2.3.1 diff --git a/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch index 0606816..b7be37b 100644 --- a/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20.2"; diff --git a/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..a08ded3 --- /dev/null +++ b/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,14 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,9 +390,9 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( +- SystemToast.SystemToastId.PACK_LOAD_FAILURE, ++ SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); diff --git a/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index ab4e4da..00fa070 100644 --- a/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java -@@ -158,7 +158,7 @@ +@@ -159,7 +159,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -9,7 +9,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -216,23 +216,24 @@ +@@ -217,23 +217,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -51,7 +51,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +241,22 @@ +@@ -241,21 +242,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -85,7 +85,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +267,16 @@ +@@ -266,15 +268,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); diff --git a/patches/1.20.2/Fabric/build.gradle.patch b/patches/1.20.2/Fabric/build.gradle.patch index 869384e..8b5c731 100644 --- a/patches/1.20.2/Fabric/build.gradle.patch +++ b/patches/1.20.2/Fabric/build.gradle.patch @@ -5,15 +5,15 @@ stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") - + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" - -@@ -116,8 +115,8 @@ +@@ -117,8 +116,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.20.2] CraterLib - ${project.version}") + setGameVersions("1.20.2") setLoaders("fabric", "quilt") diff --git a/patches/1.20.2/Forge/build.gradle.patch b/patches/1.20.2/Forge/build.gradle.patch index 9cb11ed..c1ccd3b 100644 --- a/patches/1.20.2/Forge/build.gradle.patch +++ b/patches/1.20.2/Forge/build.gradle.patch @@ -10,9 +10,9 @@ + stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") + stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +110,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +112,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index 5f8832b..a9537a9 100644 --- a/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,22 +1,45 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,6 +1,7 @@ +@@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; - import com.hypherionmc.craterlib.core.config.ModuleConfig; +-import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -@@ -28,9 +29,9 @@ ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; + import net.minecraft.client.Minecraft; + import net.minecraft.client.gui.screens.Screen; + import net.minecraftforge.client.ConfigScreenHandler; +@@ -28,17 +31,22 @@ */ @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } +- + } diff --git a/patches/1.20.2/NeoForge/build.gradle.patch b/patches/1.20.2/NeoForge/build.gradle.patch index 921f670..39e2d95 100644 --- a/patches/1.20.2/NeoForge/build.gradle.patch +++ b/patches/1.20.2/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ /dev/null -@@ -1,123 +1,0 @@ +@@ -1,133 +1,0 @@ -archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" - -dependencies { @@ -10,6 +10,8 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") - +- modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -117,10 +119,18 @@ - setVersionType("release") - setChangelog(rootProject.file("changelog.md")) - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index 6f63513..ac70abe 100644 --- a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ /dev/null -@@ -1,61 +1,0 @@ +@@ -1,69 +1,0 @@ -package com.hypherionmc.craterlib.client; - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; +-import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; -import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; -import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -55,9 +58,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.20.2/gradle.properties.patch b/patches/1.20.2/gradle.properties.patch index 9551e00..f30ff9c 100644 --- a/patches/1.20.2/gradle.properties.patch +++ b/patches/1.20.2/gradle.properties.patch @@ -23,8 +23,12 @@ # Dependencies moon_config=1.0.10 -@@ -31,18 +28,17 @@ +@@ -29,21 +26,20 @@ + adventure=4.17.0 + rpc_sdk=1.0 discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=12.0.137 # Mod Dependencies -fabrictailor=2.3.1 diff --git a/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch index 5d88b5f..7c940e8 100644 --- a/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20.4"; diff --git a/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..94e4147 --- /dev/null +++ b/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,11 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,7 +390,7 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastId.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), diff --git a/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index ab4e4da..00fa070 100644 --- a/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java -@@ -158,7 +158,7 @@ +@@ -159,7 +159,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -9,7 +9,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -216,23 +216,24 @@ +@@ -217,23 +217,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -51,7 +51,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +241,22 @@ +@@ -241,21 +242,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -85,7 +85,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +267,16 @@ +@@ -266,15 +268,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); diff --git a/patches/1.20.4/Fabric/build.gradle.patch b/patches/1.20.4/Fabric/build.gradle.patch index 5243cd1..b1f5283 100644 --- a/patches/1.20.4/Fabric/build.gradle.patch +++ b/patches/1.20.4/Fabric/build.gradle.patch @@ -1,11 +1,11 @@ --- a/Fabric/build.gradle +++ b/Fabric/build.gradle -@@ -116,8 +116,8 @@ +@@ -117,8 +117,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.20.4] CraterLib - ${project.version}") + setGameVersions("1.20.4") setLoaders("fabric", "quilt") diff --git a/patches/1.20.4/Forge/build.gradle.patch b/patches/1.20.4/Forge/build.gradle.patch index b84d05b..0f49000 100644 --- a/patches/1.20.4/Forge/build.gradle.patch +++ b/patches/1.20.4/Forge/build.gradle.patch @@ -10,9 +10,9 @@ + stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") + stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +110,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +112,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index b5243f2..a9537a9 100644 --- a/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,24 +1,45 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,8 +1,8 @@ +@@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -@@ -28,9 +28,9 @@ + import net.minecraftforge.client.ConfigScreenHandler; +@@ -28,17 +31,22 @@ */ @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } +- + } diff --git a/patches/1.20.4/NeoForge/build.gradle.patch b/patches/1.20.4/NeoForge/build.gradle.patch index adb602a..482ccc2 100644 --- a/patches/1.20.4/NeoForge/build.gradle.patch +++ b/patches/1.20.4/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ b/NeoForge/build.gradle -@@ -35,12 +35,7 @@ +@@ -37,12 +37,7 @@ unimined.minecraft { neoForged { @@ -14,7 +14,7 @@ mixinConfig("${mod_id}.mixins.json", "${mod_id}.neoforge.mixins.json") } } -@@ -59,7 +54,7 @@ +@@ -61,7 +56,7 @@ from project(":Common").sourceSets.main.resources def buildProps = project.properties.clone() @@ -23,12 +23,12 @@ expand buildProps } } -@@ -114,8 +109,8 @@ +@@ -116,8 +111,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[NeoForge 1.20.4] CraterLib - ${project.version}") + setGameVersions("1.20.4") setLoaders("neoforge") diff --git a/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index e6c55f8..5c12cf4 100644 --- a/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ b/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java -@@ -1,20 +1,11 @@ +@@ -1,23 +1,12 @@ package com.hypherionmc.craterlib.client; -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; import com.hypherionmc.craterlib.core.platform.ClientPlatform; + import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -21,7 +24,7 @@ import java.util.Objects; -@@ -45,17 +36,5 @@ +@@ -48,22 +37,5 @@ public Connection getClientConnection() { Objects.requireNonNull(Minecraft.getInstance().getConnection(), "Cannot send packets when not in game!"); return Minecraft.getInstance().getConnection().getConnection(); @@ -32,9 +35,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); } diff --git a/patches/1.20.4/gradle.properties.patch b/patches/1.20.4/gradle.properties.patch index a0bceae..f67cabb 100644 --- a/patches/1.20.4/gradle.properties.patch +++ b/patches/1.20.4/gradle.properties.patch @@ -24,7 +24,12 @@ # Dependencies moon_config=1.0.10 -@@ -32,17 +32,17 @@ +@@ -29,21 +29,21 @@ + adventure=4.17.0 + rpc_sdk=1.0 + discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=13.0.138 # Mod Dependencies fabrictailor=2.3.1 diff --git a/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch index c7b8b4e..7de7b56 100644 --- a/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch @@ -6,7 +6,7 @@ -def JDK = "21"; -def majorMc = "1.21.2"; -def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20"; diff --git a/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..a08ded3 --- /dev/null +++ b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,14 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,9 +390,9 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( +- SystemToast.SystemToastId.PACK_LOAD_FAILURE, ++ SystemToast.SystemToastIds.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), + Component.literal("Restoring Backup Copy. Check log for details")) + ); diff --git a/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index a83dd5a..e21d705 100644 --- a/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java -@@ -158,7 +158,7 @@ +@@ -159,7 +159,7 @@ return new TextConfigOption<>(Objects::toString, BigDecimal::new); } if (value instanceof ResourceLocation) { @@ -9,7 +9,7 @@ } if (isSubConfig) { return new SubConfigWidget<>(config, this, value); -@@ -179,6 +179,7 @@ +@@ -180,6 +180,7 @@ @Override public void render(@NotNull GuiGraphics matrices, int mouseX, int mouseY, float delta) { overlayBackground(matrices.pose(), TOP, height - BOTTOM, 32); @@ -17,7 +17,7 @@ renderScrollBar(); matrices.pose().pushPose(); -@@ -187,9 +188,8 @@ +@@ -188,9 +189,8 @@ overlayBackground(matrices.pose(), height - BOTTOM, height, 64); renderShadow(matrices.pose()); matrices.drawCenteredString(font, getTitle(), width / 2, 9, 0xFFFFFF); @@ -28,7 +28,7 @@ int y = (int) (TOP + 4 - Math.round(scrollerAmount)); for (Option option : options) { -@@ -216,23 +216,24 @@ +@@ -217,23 +217,24 @@ int maxY = this.height - BOTTOM; //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); @@ -70,7 +70,7 @@ RenderSystem.disableBlend(); //RenderSystem.enableTexture(); } -@@ -240,21 +241,22 @@ +@@ -241,21 +242,22 @@ private void renderShadow(PoseStack matrices) { Tesselator tesselator = Tesselator.getInstance(); @@ -104,7 +104,7 @@ //RenderSystem.enableTexture(); RenderSystem.disableBlend(); } -@@ -265,15 +267,16 @@ +@@ -266,15 +268,16 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); @@ -129,7 +129,7 @@ } public int scrollHeight() { -@@ -331,12 +334,12 @@ +@@ -332,12 +335,12 @@ } @Override diff --git a/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch index 9c1cee0..485a44b 100644 --- a/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch +++ b/patches/1.20/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java.patch @@ -8,7 +8,7 @@ /** * @author HypherionSA -@@ -22,7 +23,7 @@ +@@ -23,7 +24,7 @@ } @Override @@ -17,7 +17,7 @@ if (cancel) { setMessage(Component.translatable(screen.isEdited() ? "t.clc.cancel_discard" : "gui.cancel")); } else { -@@ -30,7 +31,7 @@ +@@ -31,7 +32,7 @@ active = screen.isEdited() && !hasErrors; setMessage(Component.translatable(hasErrors ? "t.clc.error" : "t.clc.save")); } diff --git a/patches/1.20/Fabric/build.gradle.patch b/patches/1.20/Fabric/build.gradle.patch index 23e967b..44e109d 100644 --- a/patches/1.20/Fabric/build.gradle.patch +++ b/patches/1.20/Fabric/build.gradle.patch @@ -5,15 +5,15 @@ stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}") - + modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}") modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" modImplementation "maven.modrinth:vanish:${vanish}" - -@@ -116,8 +115,8 @@ +@@ -117,8 +116,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.20/1.20.1] CraterLib - ${project.version}") + setGameVersions("1.20", "1.20.1") setLoaders("fabric", "quilt") diff --git a/patches/1.20/Forge/build.gradle.patch b/patches/1.20/Forge/build.gradle.patch index 575fd7c..c892690 100644 --- a/patches/1.20/Forge/build.gradle.patch +++ b/patches/1.20/Forge/build.gradle.patch @@ -10,9 +10,9 @@ + stupidRemapArch("dev.ftb.mods:ftb-essentials-forge:${ftb_essentials}") + stupidRemapArch("dev.ftb.mods:ftb-ranks-forge:${ftb_ranks}") - // Do not edit or remove - implementation project(":Common") -@@ -107,8 +110,8 @@ + modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") + +@@ -109,8 +112,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") diff --git a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index b5243f2..a9537a9 100644 --- a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,24 +1,45 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,8 +1,8 @@ +@@ -1,9 +1,12 @@ package com.hypherionmc.craterlib.mixin; ++import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; ++import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; ++import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -@@ -28,9 +28,9 @@ + import net.minecraftforge.client.ConfigScreenHandler; +@@ -28,17 +31,22 @@ */ @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -+ AbstractConfig config = watcher.getLeft(); - if (config.getModId().equals(selectedMod.getModId())) { - cir.setReturnValue( - Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + AbstractConfig config = watcher.getLeft(); + if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) + return; + +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); ++ if (config.getModId().equals(selectedMod.getModId())) { ++ if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config")) { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)) ++ ); ++ } else { ++ cir.setReturnValue( ++ Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) ++ ); ++ } + } + }); + } +- + } diff --git a/patches/1.20/NeoForge/build.gradle.patch b/patches/1.20/NeoForge/build.gradle.patch index 921f670..39e2d95 100644 --- a/patches/1.20/NeoForge/build.gradle.patch +++ b/patches/1.20/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ /dev/null -@@ -1,123 +1,0 @@ +@@ -1,133 +1,0 @@ -archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" - -dependencies { @@ -10,6 +10,8 @@ - stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") - stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}") - +- modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -117,10 +119,18 @@ - setVersionType("release") - setChangelog(rootProject.file("changelog.md")) - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch index 6f63513..ac70abe 100644 --- a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch +++ b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java.patch @@ -1,15 +1,18 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java +++ /dev/null -@@ -1,61 +1,0 @@ +@@ -1,69 +1,0 @@ -package com.hypherionmc.craterlib.client; - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +-import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder; -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.AbstractConfig; -import com.hypherionmc.craterlib.core.config.ConfigController; +-import com.hypherionmc.craterlib.core.config.annotations.ClothScreen; -import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; +-import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; -import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; -import com.hypherionmc.craterlib.nojang.client.BridgedOptions; -import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; @@ -55,9 +58,14 @@ - CraterEventBus.INSTANCE.postEvent(event); - - ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- AbstractConfig config = watcher.getLeft(); -- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.20/gradle.properties.patch b/patches/1.20/gradle.properties.patch index dbb345b..8239a20 100644 --- a/patches/1.20/gradle.properties.patch +++ b/patches/1.20/gradle.properties.patch @@ -23,8 +23,12 @@ # Dependencies moon_config=1.0.10 -@@ -31,18 +28,17 @@ +@@ -29,21 +26,20 @@ + adventure=4.17.0 + rpc_sdk=1.0 discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=11.1.136 # Mod Dependencies -fabrictailor=2.3.1 diff --git a/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch index 7f18f35..74dc1da 100644 --- a/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch @@ -3,7 +3,7 @@ @@ -4,7 +4,7 @@ def majorMc = "1.21.2"; def modLoaders = "neoforge|fabric|quilt|paper"; - def supportedMc = "1.21.3"; + def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def reltype = "snapshot"; diff --git a/patches/1.21.2/Forge/build.gradle.patch b/patches/1.21.2/Forge/build.gradle.patch index 6153adf..2146827 100644 --- a/patches/1.21.2/Forge/build.gradle.patch +++ b/patches/1.21.2/Forge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/Forge/build.gradle +++ /dev/null -@@ -1,116 +1,0 @@ +@@ -1,126 +1,0 @@ -// Adjust the output jar name here -archivesBaseName = "${mod_name.replace(" ", "")}-Forge-${minecraft_version}" - @@ -8,6 +8,8 @@ - // Compat - // NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}") - +- modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -116,4 +118,12 @@ - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index bd91ed3..6f86f21 100644 --- a/patches/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,6 +1,6 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,44 +1,0 @@ -package com.hypherionmc.craterlib.mixin; - -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; @@ -32,13 +32,14 @@ - @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) - private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -- if (config.getModId().equals(selectedMod.getModId())) { -- cir.setReturnValue( -- Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) -- ); -- } +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.21.2/NeoForge/build.gradle.patch b/patches/1.21.2/NeoForge/build.gradle.patch index 80ab582..4cea4ad 100644 --- a/patches/1.21.2/NeoForge/build.gradle.patch +++ b/patches/1.21.2/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ b/NeoForge/build.gradle -@@ -35,12 +35,7 @@ +@@ -37,12 +37,7 @@ unimined.minecraft { neoForged { diff --git a/patches/1.21.2/gradle.properties.patch b/patches/1.21.2/gradle.properties.patch index 29fc292..958caab 100644 --- a/patches/1.21.2/gradle.properties.patch +++ b/patches/1.21.2/gradle.properties.patch @@ -1,6 +1,6 @@ --- a/gradle.properties +++ b/gradle.properties -@@ -42,7 +42,7 @@ +@@ -43,7 +43,7 @@ # Publishing curse_id=867099 modrinth_id=Nn8Wasaq diff --git a/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch index f02ba25..2a282ba 100644 --- a/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch @@ -7,7 +7,7 @@ -def majorMc = "1.21.2"; +def majorMc = "1.21"; def modLoaders = "neoforge|fabric|quilt|paper"; --def supportedMc = "1.21.3"; +-def supportedMc = "1.21.3|1.21.4"; -def reltype = "port"; +def supportedMc = "1.21|1.21.1"; +def reltype = "snapshot"; diff --git a/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch b/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch new file mode 100644 index 0000000..94e4147 --- /dev/null +++ b/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java.patch @@ -0,0 +1,11 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/ClothConfigScreenBuilder.java +@@ -390,7 +390,7 @@ + config.saveConfig(config); + Files.deleteIfExists(backupPath); + } catch (Exception e) { +- Minecraft.getInstance().getToastManager().addToast( ++ Minecraft.getInstance().getToasts().addToast( + new SystemToast( + SystemToast.SystemToastId.PACK_LOAD_FAILURE, + Component.literal("Failed To Save Config"), diff --git a/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch b/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch index 118af03..b514a03 100644 --- a/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch +++ b/patches/1.21/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java.patch @@ -1,6 +1,6 @@ --- a/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java +++ b/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java -@@ -217,7 +217,7 @@ +@@ -218,7 +218,7 @@ //RenderSystem.disableTexture(); Tesselator tesselator = Tesselator.getInstance(); BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); @@ -9,7 +9,7 @@ buffer.addVertex(scrollbarPositionMinX, maxY, 0.0f).setColor(0, 0, 0, 255); buffer.addVertex(scrollbarPositionMaxX, maxY, 0.0f).setColor(0, 0, 0, 255); -@@ -244,7 +244,7 @@ +@@ -245,7 +245,7 @@ RenderSystem.enableBlend(); RenderSystem.blendFuncSeparate(770, 771, 0, 1); //RenderSystem.disableTexture(); @@ -18,7 +18,7 @@ Matrix4f matrix = matrices.last().pose(); buffer.addVertex(matrix, 0, TOP + 4, 0.0F).setUv(0, 1).setColor(0, 0, 0, 0); buffer.addVertex(matrix, width, TOP + 4, 0.0F).setUv(1, 1).setColor(0, 0, 0, 0); -@@ -266,7 +266,7 @@ +@@ -267,7 +267,7 @@ protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { Tesselator tesselator = Tesselator.getInstance(); BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); diff --git a/patches/1.21/Fabric/build.gradle.patch b/patches/1.21/Fabric/build.gradle.patch index 7b7f743..3599791 100644 --- a/patches/1.21/Fabric/build.gradle.patch +++ b/patches/1.21/Fabric/build.gradle.patch @@ -1,11 +1,11 @@ --- a/Fabric/build.gradle +++ b/Fabric/build.gradle -@@ -116,8 +116,8 @@ +@@ -117,8 +117,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") + setGameVersions("1.21", "1.21.1") setLoaders("fabric", "quilt") diff --git a/patches/1.21/Forge/build.gradle.patch b/patches/1.21/Forge/build.gradle.patch index 6153adf..2146827 100644 --- a/patches/1.21/Forge/build.gradle.patch +++ b/patches/1.21/Forge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/Forge/build.gradle +++ /dev/null -@@ -1,116 +1,0 @@ +@@ -1,126 +1,0 @@ -// Adjust the output jar name here -archivesBaseName = "${mod_name.replace(" ", "")}-Forge-${minecraft_version}" - @@ -8,6 +8,8 @@ - // Compat - // NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}") - +- modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}") +- - // Do not edit or remove - implementation project(":Common") -} @@ -116,4 +118,12 @@ - setArtifact(remapJar) - setCurseEnvironment("both") - setIsManualRelease(true) +- +- curseDepends { +- optional("cloth-config") +- } +- +- modrinthDepends { +- optional("cloth-config") +- } -} diff --git a/patches/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index bd91ed3..6f86f21 100644 --- a/patches/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,6 +1,6 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,44 +1,0 @@ -package com.hypherionmc.craterlib.mixin; - -import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; @@ -32,13 +32,14 @@ - @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) - private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { -- if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; -- if (config.getModId().equals(selectedMod.getModId())) { -- cir.setReturnValue( -- Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) -- ); -- } +- AbstractConfig config = watcher.getLeft(); +- if (config.getClass().isAnnotationPresent(NoConfigScreen.class)) +- return; +- +- if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) { +- ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen)))); +- } else { +- //ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); - } diff --git a/patches/1.21/NeoForge/build.gradle.patch b/patches/1.21/NeoForge/build.gradle.patch index 5c92e87..9f152d8 100644 --- a/patches/1.21/NeoForge/build.gradle.patch +++ b/patches/1.21/NeoForge/build.gradle.patch @@ -1,6 +1,6 @@ --- a/NeoForge/build.gradle +++ b/NeoForge/build.gradle -@@ -35,12 +35,7 @@ +@@ -37,12 +37,7 @@ unimined.minecraft { neoForged { @@ -14,12 +14,12 @@ mixinConfig("${mod_id}.mixins.json", "${mod_id}.neoforge.mixins.json") } } -@@ -114,8 +109,8 @@ +@@ -116,8 +111,8 @@ setVersionType("release") setChangelog(rootProject.file("changelog.md")) setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}") -- setGameVersions("1.21.3") +- setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}") +- setGameVersions("1.21.3", "1.21.4") + setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") + setGameVersions("1.21", "1.21.1") setLoaders("neoforge") diff --git a/patches/1.21/gradle.properties.patch b/patches/1.21/gradle.properties.patch index 9e1e589..5319df1 100644 --- a/patches/1.21/gradle.properties.patch +++ b/patches/1.21/gradle.properties.patch @@ -16,7 +16,16 @@ # Forge forge_version=50.0.6 -@@ -42,7 +42,7 @@ +@@ -29,7 +29,7 @@ + adventure=4.17.0 + rpc_sdk=1.0 + discord_formatter=2.0.0 +-cloth_config=17.0.144 ++cloth_config=15.0.140 + + # Mod Dependencies + fabrictailor=2.3.1 +@@ -43,7 +43,7 @@ # Publishing curse_id=867099 modrinth_id=Nn8Wasaq