From 614fb0bc49337cf66282726263ba467d1135a372 Mon Sep 17 00:00:00 2001 From: hypherionmc Date: Sat, 10 Aug 2024 15:17:10 +0200 Subject: [PATCH] - [FEAT] New APIs for Maintenance Mode and rewrite commands system - [FEAT] Improved config system to fix old loading bugs and support JSON - [FEAT] LuckPerms support for commands --- .../craterlib/api/commands/CraterCommand.java | 145 +++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../ServerStatusPacketListenerMixin.java | 46 ++++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 42 +++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 4 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 6 +- .../src/main/resources/craterlib_logo.png | Bin 49343 -> 0 bytes 1.18.2/README.md | 2 +- 1.18.2/build.gradle | 2 + 1.18.2/gradle.properties | 4 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../ServerStatusPacketListenerMixin.java | 46 ++++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 43 ++++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 4 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 6 +- .../src/main/resources/craterlib_logo.png | Bin 49343 -> 0 bytes 1.19.2/README.md | 2 +- 1.19.2/build.gradle | 2 + 1.19.2/gradle.properties | 4 +- 1.19.3/.jenkins/Jenkinsfile.snapshot | 2 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 31 ++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 3 +- 1.19.3/Fabric/build.gradle | 4 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../ServerStatusPacketListenerMixin.java | 52 +++++++ .../resources/craterlib.fabric.mixins.json | 3 +- .../Fabric/src/main/resources/fabric.mod.json | 2 +- 1.19.3/Forge/build.gradle | 4 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 5 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../src/main/resources/META-INF/mods.toml | 6 +- .../resources/craterlib.forge.mixins.json | 3 +- .../src/main/resources/craterlib_logo.png | Bin 49343 -> 0 bytes 1.19.3/README.md | 4 +- 1.19.3/build.gradle | 2 + 1.19.3/gradle.properties | 10 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 31 ++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 3 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 52 +++++++ .../resources/craterlib.fabric.mixins.json | 3 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 5 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.forge.mixins.json | 3 +- .../src/main/resources/craterlib_logo.png | Bin 49343 -> 0 bytes 1.20.2/README.md | 2 +- 1.20.2/build.gradle | 2 + 1.20.2/gradle.properties | 4 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 31 ++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 3 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 52 +++++++ .../resources/craterlib.fabric.mixins.json | 3 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 6 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.forge.mixins.json | 3 +- .../common/NeoForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 5 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.neoforge.mixins.json | 3 +- 1.20.4/README.md | 2 +- 1.20.4/build.gradle | 2 + 1.20.4/gradle.properties | 4 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 31 ++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 3 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../ServerStatusPacketListenerMixin.java | 52 +++++++ .../resources/craterlib.fabric.mixins.json | 3 +- .../craterlib/common/ForgeServerEvents.java | 4 +- .../mixin/ConfigScreenHandlerMixin.java | 6 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.forge.mixins.json | 3 +- .../src/main/resources/craterlib_logo.png | Bin 49343 -> 0 bytes 1.20/README.md | 2 +- 1.20/build.gradle | 2 + 1.20/gradle.properties | 4 +- .../craterlib/api/commands/CraterCommand.java | 144 ++++++++++++++--- .../api/events/server/CraterPlayerEvent.java | 1 - .../server/CraterRegisterCommandEvent.java | 11 +- .../api/events/server/ServerStatusEvent.java | 35 +++++ .../client/gui/config/CraterConfigScreen.java | 8 +- .../gui/config/widgets/SubConfigWidget.java | 6 +- .../craterlib/compat/LuckPermsCompat.java | 20 +++ .../craterlib/core/config/AbstractConfig.java | 71 +++++++++ .../core/config/ConfigController.java | 28 +++- .../craterlib/core/config/ModuleConfig.java | 98 ++---------- .../config/formats/AbstractConfigFormat.java | 32 ++++ .../core/config/formats/JsonConfigFormat.java | 67 ++++++++ .../core/config/formats/TomlConfigFormat.java | 67 ++++++++ .../mixin/events/ServerStatusMixin.java | 27 ++++ .../commands/BridgedCommandSourceStack.java | 4 + .../nojang/commands/CommandsRegistry.java | 85 ---------- .../protocol/status/WrappedServerStatus.java | 31 ++++ .../world/entity/player/BridgedPlayer.java | 5 + .../craterlib/utils/ChatUtils.java | 7 + .../src/main/resources/craterlib.mixins.json | 3 +- 1.21/Fabric/build.gradle | 4 +- .../craterlib/CraterLibInitializer.java | 4 +- .../CraterLibModMenuIntegration.java | 5 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 52 +++++++ .../resources/craterlib.fabric.mixins.json | 3 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.forge.mixins.json | 3 +- 1.21/NeoForge/build.gradle | 4 +- .../client/NeoForgeClientHelper.java | 6 +- .../common/NeoForgeServerEvents.java | 4 +- .../ServerGamePacketListenerImplMixin.java | 4 +- .../ServerStatusPacketListenerMixin.java | 53 +++++++ .../resources/craterlib.neoforge.mixins.json | 3 +- 1.21/README.md | 3 +- 1.21/build.gradle | 2 + 1.21/gradle.properties | 4 +- commit.sha | 2 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../api/commands/CraterCommand.java.patch | 77 ++++++++++ .../gui/config/CraterConfigScreen.java.patch | 2 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../mixin/events/ServerStatusMixin.java.patch | 20 +++ ...ServerStatusPacketListenerMixin.java.patch | 49 ++++++ .../BridgedCommandSourceStack.java.patch | 2 +- .../commands/CommandsRegistry.java.patch | 11 -- .../status/WrappedServerStatus.java.patch | 48 ++++++ .../craterlib/utils/ChatUtils.java.patch | 2 +- .../resources/craterlib.mixins.json.patch | 12 ++ patches/1.18.2/Fabric/build.gradle.patch | 4 +- .../craterlib/CraterLibInitializer.java.patch | 10 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 55 +++++++ .../craterlib.fabric.mixins.json.patch | 12 ++ .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 18 ++- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.forge.mixins.json.patch | 12 ++ patches/1.18.2/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../common/NeoForgeServerEvents.java.patch | 6 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.neoforge.mixins.json.patch | 5 +- patches/1.18.2/README.md.patch | 6 +- patches/1.18.2/build.gradle.patch | 4 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../mixin/events/ServerStatusMixin.java.patch | 20 +++ ...ServerStatusPacketListenerMixin.java.patch | 49 ++++++ .../BridgedCommandSourceStack.java.patch | 2 +- .../status/WrappedServerStatus.java.patch | 46 ++++++ .../resources/craterlib.mixins.json.patch | 12 ++ patches/1.19.2/Fabric/build.gradle.patch | 4 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 55 +++++++ .../craterlib.fabric.mixins.json.patch | 12 ++ .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 24 +++ ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.forge.mixins.json.patch | 12 ++ patches/1.19.2/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../common/NeoForgeServerEvents.java.patch | 6 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.neoforge.mixins.json.patch | 5 +- patches/1.19.2/README.md.patch | 6 +- patches/1.19.2/build.gradle.patch | 4 +- .../.jenkins/Jenkinsfile.snapshot.patch | 4 +- .../config/widgets/SubConfigWidget.java.patch | 2 +- .../BridgedCommandSourceStack.java.patch | 2 +- patches/1.19.3/Fabric/build.gradle.patch | 8 +- ...rverGamePacketListenerImplMixin.java.patch | 5 +- .../src/main/resources/fabric.mod.json.patch | 2 +- patches/1.19.3/Forge/build.gradle.patch | 4 +- .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 22 +++ ...rverGamePacketListenerImplMixin.java.patch | 8 +- ...ServerStatusPacketListenerMixin.java.patch | 11 ++ .../main/resources/META-INF/mods.toml.patch | 6 +- patches/1.19.3/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../common/NeoForgeServerEvents.java.patch | 6 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.neoforge.mixins.json.patch | 5 +- patches/1.19.3/README.md.patch | 4 +- patches/1.19.3/build.gradle.patch | 4 +- patches/1.19.3/gradle.properties.patch | 6 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- patches/1.20.2/Fabric/build.gradle.patch | 4 +- .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 22 +++ ...ServerStatusPacketListenerMixin.java.patch | 11 ++ patches/1.20.2/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../common/NeoForgeServerEvents.java.patch | 6 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.neoforge.mixins.json.patch | 5 +- patches/1.20.2/README.md.patch | 6 +- patches/1.20.2/build.gradle.patch | 4 +- .../.jenkins/Jenkinsfile.snapshot.patch | 2 +- patches/1.20.4/Fabric/build.gradle.patch | 4 +- .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 24 +++ ...ServerStatusPacketListenerMixin.java.patch | 11 ++ patches/1.20.4/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../mixin/ConfigScreenHandlerMixin.java.patch | 7 +- .../craterlib.neoforge.mixins.json.patch | 4 +- patches/1.20.4/README.md.patch | 6 +- .../1.20/.jenkins/Jenkinsfile.snapshot.patch | 2 +- patches/1.20/Fabric/build.gradle.patch | 4 +- ...rverGamePacketListenerImplMixin.java.patch | 10 +- .../common/ForgeServerEvents.java.patch | 20 +++ .../mixin/ConfigScreenHandlerMixin.java.patch | 24 +++ ...rverGamePacketListenerImplMixin.java.patch | 10 +- ...ServerStatusPacketListenerMixin.java.patch | 11 ++ patches/1.20/NeoForge/build.gradle.patch | 4 +- .../client/NeoForgeClientHelper.java.patch | 6 +- .../common/NeoForgeServerEvents.java.patch | 6 +- ...rverGamePacketListenerImplMixin.java.patch | 4 +- ...ServerStatusPacketListenerMixin.java.patch | 56 +++++++ .../craterlib.neoforge.mixins.json.patch | 5 +- patches/1.20/README.md.patch | 6 +- patches/1.20/build.gradle.patch | 4 +- .../1.21/.jenkins/Jenkinsfile.snapshot.patch | 6 +- patches/1.21/README.md.patch | 10 ++ 352 files changed, 6045 insertions(+), 1798 deletions(-) create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java delete mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java delete mode 100644 1.18.2/NeoForge/src/main/resources/craterlib_logo.png create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java delete mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java delete mode 100644 1.19.2/NeoForge/src/main/resources/craterlib_logo.png create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java delete mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java create mode 100644 1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java delete mode 100644 1.19.3/NeoForge/src/main/resources/craterlib_logo.png create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java delete mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java create mode 100644 1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java delete mode 100644 1.20.2/NeoForge/src/main/resources/craterlib_logo.png create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java delete mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java create mode 100644 1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java delete mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java create mode 100644 1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java delete mode 100644 1.20/NeoForge/src/main/resources/craterlib_logo.png create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java delete mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java create mode 100644 1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java create mode 100644 1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch delete mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java.patch create mode 100644 patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch create mode 100644 patches/1.18.2/Common/src/main/resources/craterlib.mixins.json.patch create mode 100644 patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.18.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch create mode 100644 patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.18.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch create mode 100644 patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch create mode 100644 patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch create mode 100644 patches/1.19.2/Common/src/main/resources/craterlib.mixins.json.patch create mode 100644 patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch create mode 100644 patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch create mode 100644 patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch create mode 100644 patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch create mode 100644 patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch create mode 100644 patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch create mode 100644 patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch create mode 100644 patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch create mode 100644 patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch create mode 100644 patches/1.21/README.md.patch 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 cc4969e..ee66687 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,53 +1,156 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayerOrException()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !(stack.getEntity() instanceof Player) || luckPermNode.isEmpty()) + return stack.hasPermission(this.permLevel); + + return LuckPermsCompat.INSTANCE.hasPermission((ServerPlayer) stack.getEntity(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 fbabb4b..8b3da68 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -45,11 +45,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(new TranslatableComponent("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -60,7 +60,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 b4fa4f9..db60620 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.TranslatableComponent; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..2d3999c --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "getFavicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(event.getNewIcon().get().toMojang()); + } + } + +} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..0a18a4f --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java @@ -0,0 +1,46 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow @Final private Connection connection; + + @Shadow @Final private MinecraftServer server; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(server.getStatus().getDescription())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + ServerStatus serverStatus = this.server.getStatus(); + serverStatus.setDescription(ChatUtils.adventureToMojang(event.getNewStatus())); + + this.connection.send(new ClientboundStatusResponsePacket(serverStatus)); + } + } + +} \ No newline at end of file diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index b14e484..d50bf58 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 7e777f7..0000000 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayerOrException()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..38cad97 --- /dev/null +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,42 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import org.jetbrains.annotations.ApiStatus; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final String internal; + private final byte[] iconBytes; + + public WrappedFavicon(byte[] iconBytes) { + this.iconBytes = iconBytes; + + if (iconBytes != null) { + byte[] encoded = Base64.getEncoder().encode(iconBytes); + internal = "data:image/png;base64," + new String(encoded, StandardCharsets.UTF_8); + } else { + internal = null; + } + } + + @ApiStatus.Internal + public WrappedFavicon(String internal) { + this.internal = internal; + this.iconBytes = new byte[0]; + } + + public byte[] iconBytes() { + return iconBytes; + } + + public String 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index 634d857..508a905 100644 --- a/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -95,4 +95,11 @@ public class ChatUtils { return mojangToAdventure(new TranslatableComponent(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.18.2/Common/src/main/resources/craterlib.mixins.json b/1.18.2/Common/src/main/resources/craterlib.mixins.json index 1a739db..1462775 100644 --- a/1.18.2/Common/src/main/resources/craterlib.mixins.json +++ b/1.18.2/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,9 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin", + "events.ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index f951a1c..92bceff 100644 --- a/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 c61993f..df8a51a 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,8 +1,8 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -28,9 +28,9 @@ public class ConfigScreenHandlerMixin { */ @Inject(at = @At("RETURN"), method = "getGuiFactoryFor", 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)) diff --git a/1.18.2/NeoForge/src/main/resources/craterlib_logo.png b/1.18.2/NeoForge/src/main/resources/craterlib_logo.png deleted file mode 100644 index ce0159cc4aa4d823ea354042e531b4522d6dfead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49343 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 z z0_4I}B3{&0Z2$_UK|l6$_F5GbB1!e;?<`^ga;fFGMz-Se^MAW>Keh~s)aWsZza-Wa zh8FX{!yiI)Ljp=gIiG3cbs*3I7#lyPS*q6+XL$;krfa|T{j!X7NO%9Jw8jjQ^oGyb zuB>(Ay2y58m-xwl&;PxSaJT2vTCB#G6Yc?K6DubK#C89Og8@02Yf7+4Vmz5*NEo{& z&>W3XcH_uv{$Z45Oo+-NT$_6P>JUFKFv(gRqcz%^lx6572_M)nTWIO~h}&HFE{_}5 z8R|z4_}lCMQBU{Gd3?a4|9JCe_ew+2=R30OVOp1-?E`3=QwNM{jwLdE9Frn+%Q*$o zY-BRKU036=wxizwcph`BWI3Q^ms~Qk*&UewhP~N@df3sHJJT~7s=j`y^C&-a*BQz{ zUfNXagXU($%NqlFZDuykU1X#WZ_ow}eCTYw!3sFi-uAbIrzSa$1$QWY3^E|wM}@Ol z4BW+h^o;(}F`L>JY_v@ev&zsG194dotf7rHJTNeAQ~pNjVns}f!B?0szcS39HePIg z_wxP*(=Xeb$9Y5fV_b73G_il zXrSUx5E;tj(;!?eRdU+o*>h&72`G1&OU4{v4 zBM69zj5>>~lqkyBnHz9%3h2rC>P0E7M#!;M%}dCZ3{P zesubLp#ul3fqk?ZFeI+q%=?jY2glsYA>B6M-r!HqjRTSV;N0e=ulsLzKQi3rop&z3 zH;OHYJqan7<>>8y!_AGE$IU@Ct?|tdVpjnV&HVtf9S8T{lr1>M%n&8bazr#QxHe;< z+0=m4Q#3_|K%w|k&#HOT3hb1$#>HuRPo&*ju2UQcx&%6aH7J-;5;RG{r)uU|2ur7R z(9>i-B`X<0L0r?4>o&#P zAUra7SR&%Gp~?Ft&+{F)fCUjY&8x$@3;BPe^LC^=tG9csyL&Aa9?S$4uT%ye+iIRG zmNV!U42y%)FSuyl>HuNdKC#D;*w?j32J-D4$sK;bGQ_{n;$Fc3>RZO30i&ui2pxGX zWkF_hWr~P{+z+q`$HJnXo|lHlIC%I%qQ&U2@8ndyUDf(v9?@e&A!yQ(7_6Go$Q)CRNo z9}@w|%_mrl+}ioP`BSJ;eDly8`El>9JJbC6Oal#U0*u;%KWdR%aLwqIspxB2oF^oY z#<*A`D{k!|yW)obE;hb%qmIC^-Dk{*)?Cv2*^%OwFfD01CKw_Ee{_r9cIXAJ{p4oSiu;rX`J@;Ts29_--I|12eLb5ozFTzJf7R(FQjkK+59+`Nyxt6XV$p zS+d9cnNUyQ2jY6rjJQ~!HTUrn)&T}(-OnA^@3h@jP8_D{6MOm!f8b#LzJSUAKT4)ha9-WL$F&}@MME_!hG1YG)Ju~6e<7)tbYYw$a}uw3feLj zHx_9d#XnnHdHyK@3==#-i3G3?7`F&F6jg!m%^~WgRIMGJz+9SqhCy{7^V?Zr5lrqe zE*E2krwZF1&g7r>TV#?~ynBjY*@dP1OJ24(I#3nde2E^Mf%S~Ot|VwY0TP)j#oS7j zJ+LfR;dC>Z-J!ngPS)g53T^@_Ggh}qvR-&jy(V^V1`b@pJwKryM}3{+h3=_~c}w-s zmrqf)%trzjXpQ5odUbjqhSM?c7sX1S+g|kKjAvxwXnt!vu_25Ll$(I_R&0kiUK{sw zYkr`_S;cu^>MG=3rMC5aD#ocTL)YtiF6f6-DAu!`P4ub4xi}K95yQJB+#^=6CELF} ze9n~5+AX3lb{@H9HcrSnT=7O@5qQoV^BU}f=7Rso;Tf6G-plzq<9#hMqe1%4hm^i> zVpMoQ^_-5vyM`IDof|_87+jQos8s?(c{fI+|D@|s!e0V!R$cEn@aOXb^?;k;Up-_0j z%+y{j(M4Fvxxa1?rq>gMJ0lo zZnLCaDlCc)wV-hO*vb)z z{Aw?4m?<_im&T4DuQQ{yl(LQr;7FI;F+?)cr@tNK5!uHuIS#OwJs!X&AOamCp!T^2 zwMGCc^c<0U3Ak%R`5E2&)DL^W^dCT1G7Laq0>FM7aIJI_MrRVh2$CsG0o|WQChE5s z>=MUftW>@RRD6e#X1N}_BMO(5ZvRQ6OL_#W04zkzODy==r&0E(3?1sjUueloD8S1- zEgm|ZRWJ@!$e@gCCe${7>Y|-=NEBmizm979@!9Ad*s1B#LL46QzEXvLo6!uSqjmfC zOy~kNtQDqafvAWRo1CKk>`?6$S=qeFKpJuj#dJ_6C?_LhA-ynR_$GRnfPjkd^-B)8QTHjsGseoBoPtJi@JAII=+z1cLU5~kLwm4O(A@$K=lt|$zg*|K@{&AJBfwIZufXlAxV`NbN?8G()+jo^HH;bmZ8&+Y zpJ#+AQ2_9UcSpiH&+I?leumlSZf|B(KkK(?x?AomuAN9E^j;K}wo7KI4imBGRfEIh zaReC3hk_>9cL>SyO_2w2_ky3QrUgS4!1z%)-)f}NN43xA#Vh2fTYq8hTEIVLfbHSbf5$-m-ZJ_eqw>#~Wu z35oW!EjG6()?RI7vKVp z5Yz(N2`~JU%M?_8ge74Q3b{FNgqWYQ)PK|E`X`Timke_g`^Q$up|U7xVOrfkoFj!& z1DK>s^3`b%`k3>e-|{W|%@edE3^Siq?f-r251ZFBFvZ0SpB4FI zgxx#|VN}}j-XHH*he9>ps#>*sx!X!`U$z?VW@_G6PQA zu_BxYUmKTq;!A+57$;K#6&udHYnG%ozi>!3Uk#J-YNq5W2xJ7Y*r^SJ6r zNM#6vzkB(7`H(|)B`Bsr+TmUnj2$z$_JCtD>ou+9exFOER_$34Yu4cp^y%RaeEOT0 z=KUqZH?F1V;ioU(qV1c1lK1!b#Rp*f(mT>MT2ud4msbpL0#DHNhCjjli@Xxjb2fyJ zH;AV-lFai-<;aLb;)wUT{|+Zincr~E<3L-|z2Kr%0pV5p+_BJ$eCFnLYbsAK#GASQ zDf;&|uLRABEj?0;&tEOv!l$~^F8-DtZ%&7|QbZ-2_otcNHj<$2OiM~d*p;r!4wa!I zl*1>R|GkT6$A{u>kTwL%G4gH{!voK=8mTu=Lnt)m9Yn85sJGF|=CIx;gRiOIOQ_4I zQ&v2tZxVx#`9wR=`_OOy@iE$=FL9_U&RvCxm1yW;&x`xL_cT*Fx@DDMsMa|*oRW4<`TU7L}1 zd87?L{p)sZ`1{=FExUhpSML>rymDP!5c_ZVa(HQvz`m@lzjc`fLGzg4x>m#A_k(NS z1+jYqs2AL{&sB*r;UttXfU2@q{5p>SEIi@m=oS}V>y_O;qs}Qh>XV2^*ABoF9}+Wc z=|NSe9)9$WoURbZX`x@X|K9aOtQ}sUraok~lp0o0a-HT~Pw2Q$U;L>k;C-HsK(1Hjg#g}mYiI6?|ht{qKC6VnHn zf4BKoz8Gk{bbuzk(i9M>S-|ZfOx>eV>aD05kSy67-t0uhyTed4$kNo%uu8r))?6vW;lWz@S%Ih#S|?0?;b^qdqdP}t0NI=@b<07 zVUB~e{wkMSPdU6@Mjh`J=(4*H^kIobA0g~Fpw2t^cF}DLm!e2%N?DzF*u;WJ^+Er! zegF>;Cdaiu4yxYROZ%$?U;hGws-mhoyc?d<*po)F&j=CSw13Fq5O$P#AQ5?Cduj__ zgC*q}hbrFvkU@D{Ma(c{s?1itmC45RW6p4`NYNgC%}kuhIlJShXnjjeS5wUl$2%NA zTJn{xl?hEdf%=9Y^O(GO(;k8Y9D+iZ*GI_F5fM~ydu*%bm7~)Jya?!O8EBTPJB0rq De~rmR diff --git a/1.18.2/README.md b/1.18.2/README.md index 49c0699..4b18d17 100644 --- a/1.18.2/README.md +++ b/1.18.2/README.md @@ -15,7 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.18.2/build.gradle b/1.18.2/build.gradle index 3fb1410..c222e4f 100644 --- a/1.18.2/build.gradle +++ b/1.18.2/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.18.1:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.18.2/gradle.properties b/1.18.2/gradle.properties index bf52fa4..4882a58 100644 --- a/1.18.2/gradle.properties +++ b/1.18.2/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 823f2f5..4345c2d 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -43,11 +43,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -58,7 +58,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 a4d229d..6ead50a 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..2d3999c --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "getFavicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(event.getNewIcon().get().toMojang()); + } + } + +} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..0a18a4f --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java @@ -0,0 +1,46 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow @Final private Connection connection; + + @Shadow @Final private MinecraftServer server; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(server.getStatus().getDescription())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + ServerStatus serverStatus = this.server.getStatus(); + serverStatus.setDescription(ChatUtils.adventureToMojang(event.getNewStatus())); + + this.connection.send(new ClientboundStatusResponsePacket(serverStatus)); + } + } + +} \ No newline at end of file diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index b14e484..d50bf58 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..12e79c7 --- /dev/null +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,43 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final String internal; + private final byte[] iconBytes; + + public WrappedFavicon(byte[] iconBytes) { + this.iconBytes = iconBytes; + + if (iconBytes != null) { + byte[] encoded = Base64.getEncoder().encode(iconBytes); + internal = "data:image/png;base64," + new String(encoded, StandardCharsets.UTF_8); + } else { + internal = null; + } + } + + @ApiStatus.Internal + public WrappedFavicon(String internal) { + this.internal = internal; + this.iconBytes = new byte[0]; + } + + public byte[] iconBytes() { + return iconBytes; + } + + public String 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index af9736d..5759fde 100644 --- a/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -92,4 +92,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.19.2/Common/src/main/resources/craterlib.mixins.json b/1.19.2/Common/src/main/resources/craterlib.mixins.json index 1a739db..1462775 100644 --- a/1.19.2/Common/src/main/resources/craterlib.mixins.json +++ b/1.19.2/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,9 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin", + "events.ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 927bd23..d77853c 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,8 +1,8 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -28,9 +28,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.19.2/NeoForge/src/main/resources/craterlib_logo.png b/1.19.2/NeoForge/src/main/resources/craterlib_logo.png deleted file mode 100644 index ce0159cc4aa4d823ea354042e531b4522d6dfead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49343 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 z z0_4I}B3{&0Z2$_UK|l6$_F5GbB1!e;?<`^ga;fFGMz-Se^MAW>Keh~s)aWsZza-Wa zh8FX{!yiI)Ljp=gIiG3cbs*3I7#lyPS*q6+XL$;krfa|T{j!X7NO%9Jw8jjQ^oGyb zuB>(Ay2y58m-xwl&;PxSaJT2vTCB#G6Yc?K6DubK#C89Og8@02Yf7+4Vmz5*NEo{& z&>W3XcH_uv{$Z45Oo+-NT$_6P>JUFKFv(gRqcz%^lx6572_M)nTWIO~h}&HFE{_}5 z8R|z4_}lCMQBU{Gd3?a4|9JCe_ew+2=R30OVOp1-?E`3=QwNM{jwLdE9Frn+%Q*$o zY-BRKU036=wxizwcph`BWI3Q^ms~Qk*&UewhP~N@df3sHJJT~7s=j`y^C&-a*BQz{ zUfNXagXU($%NqlFZDuykU1X#WZ_ow}eCTYw!3sFi-uAbIrzSa$1$QWY3^E|wM}@Ol z4BW+h^o;(}F`L>JY_v@ev&zsG194dotf7rHJTNeAQ~pNjVns}f!B?0szcS39HePIg z_wxP*(=Xeb$9Y5fV_b73G_il zXrSUx5E;tj(;!?eRdU+o*>h&72`G1&OU4{v4 zBM69zj5>>~lqkyBnHz9%3h2rC>P0E7M#!;M%}dCZ3{P zesubLp#ul3fqk?ZFeI+q%=?jY2glsYA>B6M-r!HqjRTSV;N0e=ulsLzKQi3rop&z3 zH;OHYJqan7<>>8y!_AGE$IU@Ct?|tdVpjnV&HVtf9S8T{lr1>M%n&8bazr#QxHe;< z+0=m4Q#3_|K%w|k&#HOT3hb1$#>HuRPo&*ju2UQcx&%6aH7J-;5;RG{r)uU|2ur7R z(9>i-B`X<0L0r?4>o&#P zAUra7SR&%Gp~?Ft&+{F)fCUjY&8x$@3;BPe^LC^=tG9csyL&Aa9?S$4uT%ye+iIRG zmNV!U42y%)FSuyl>HuNdKC#D;*w?j32J-D4$sK;bGQ_{n;$Fc3>RZO30i&ui2pxGX zWkF_hWr~P{+z+q`$HJnXo|lHlIC%I%qQ&U2@8ndyUDf(v9?@e&A!yQ(7_6Go$Q)CRNo z9}@w|%_mrl+}ioP`BSJ;eDly8`El>9JJbC6Oal#U0*u;%KWdR%aLwqIspxB2oF^oY z#<*A`D{k!|yW)obE;hb%qmIC^-Dk{*)?Cv2*^%OwFfD01CKw_Ee{_r9cIXAJ{p4oSiu;rX`J@;Ts29_--I|12eLb5ozFTzJf7R(FQjkK+59+`Nyxt6XV$p zS+d9cnNUyQ2jY6rjJQ~!HTUrn)&T}(-OnA^@3h@jP8_D{6MOm!f8b#LzJSUAKT4)ha9-WL$F&}@MME_!hG1YG)Ju~6e<7)tbYYw$a}uw3feLj zHx_9d#XnnHdHyK@3==#-i3G3?7`F&F6jg!m%^~WgRIMGJz+9SqhCy{7^V?Zr5lrqe zE*E2krwZF1&g7r>TV#?~ynBjY*@dP1OJ24(I#3nde2E^Mf%S~Ot|VwY0TP)j#oS7j zJ+LfR;dC>Z-J!ngPS)g53T^@_Ggh}qvR-&jy(V^V1`b@pJwKryM}3{+h3=_~c}w-s zmrqf)%trzjXpQ5odUbjqhSM?c7sX1S+g|kKjAvxwXnt!vu_25Ll$(I_R&0kiUK{sw zYkr`_S;cu^>MG=3rMC5aD#ocTL)YtiF6f6-DAu!`P4ub4xi}K95yQJB+#^=6CELF} ze9n~5+AX3lb{@H9HcrSnT=7O@5qQoV^BU}f=7Rso;Tf6G-plzq<9#hMqe1%4hm^i> zVpMoQ^_-5vyM`IDof|_87+jQos8s?(c{fI+|D@|s!e0V!R$cEn@aOXb^?;k;Up-_0j z%+y{j(M4Fvxxa1?rq>gMJ0lo zZnLCaDlCc)wV-hO*vb)z z{Aw?4m?<_im&T4DuQQ{yl(LQr;7FI;F+?)cr@tNK5!uHuIS#OwJs!X&AOamCp!T^2 zwMGCc^c<0U3Ak%R`5E2&)DL^W^dCT1G7Laq0>FM7aIJI_MrRVh2$CsG0o|WQChE5s z>=MUftW>@RRD6e#X1N}_BMO(5ZvRQ6OL_#W04zkzODy==r&0E(3?1sjUueloD8S1- zEgm|ZRWJ@!$e@gCCe${7>Y|-=NEBmizm979@!9Ad*s1B#LL46QzEXvLo6!uSqjmfC zOy~kNtQDqafvAWRo1CKk>`?6$S=qeFKpJuj#dJ_6C?_LhA-ynR_$GRnfPjkd^-B)8QTHjsGseoBoPtJi@JAII=+z1cLU5~kLwm4O(A@$K=lt|$zg*|K@{&AJBfwIZufXlAxV`NbN?8G()+jo^HH;bmZ8&+Y zpJ#+AQ2_9UcSpiH&+I?leumlSZf|B(KkK(?x?AomuAN9E^j;K}wo7KI4imBGRfEIh zaReC3hk_>9cL>SyO_2w2_ky3QrUgS4!1z%)-)f}NN43xA#Vh2fTYq8hTEIVLfbHSbf5$-m-ZJ_eqw>#~Wu z35oW!EjG6()?RI7vKVp z5Yz(N2`~JU%M?_8ge74Q3b{FNgqWYQ)PK|E`X`Timke_g`^Q$up|U7xVOrfkoFj!& z1DK>s^3`b%`k3>e-|{W|%@edE3^Siq?f-r251ZFBFvZ0SpB4FI zgxx#|VN}}j-XHH*he9>ps#>*sx!X!`U$z?VW@_G6PQA zu_BxYUmKTq;!A+57$;K#6&udHYnG%ozi>!3Uk#J-YNq5W2xJ7Y*r^SJ6r zNM#6vzkB(7`H(|)B`Bsr+TmUnj2$z$_JCtD>ou+9exFOER_$34Yu4cp^y%RaeEOT0 z=KUqZH?F1V;ioU(qV1c1lK1!b#Rp*f(mT>MT2ud4msbpL0#DHNhCjjli@Xxjb2fyJ zH;AV-lFai-<;aLb;)wUT{|+Zincr~E<3L-|z2Kr%0pV5p+_BJ$eCFnLYbsAK#GASQ zDf;&|uLRABEj?0;&tEOv!l$~^F8-DtZ%&7|QbZ-2_otcNHj<$2OiM~d*p;r!4wa!I zl*1>R|GkT6$A{u>kTwL%G4gH{!voK=8mTu=Lnt)m9Yn85sJGF|=CIx;gRiOIOQ_4I zQ&v2tZxVx#`9wR=`_OOy@iE$=FL9_U&RvCxm1yW;&x`xL_cT*Fx@DDMsMa|*oRW4<`TU7L}1 zd87?L{p)sZ`1{=FExUhpSML>rymDP!5c_ZVa(HQvz`m@lzjc`fLGzg4x>m#A_k(NS z1+jYqs2AL{&sB*r;UttXfU2@q{5p>SEIi@m=oS}V>y_O;qs}Qh>XV2^*ABoF9}+Wc z=|NSe9)9$WoURbZX`x@X|K9aOtQ}sUraok~lp0o0a-HT~Pw2Q$U;L>k;C-HsK(1Hjg#g}mYiI6?|ht{qKC6VnHn zf4BKoz8Gk{bbuzk(i9M>S-|ZfOx>eV>aD05kSy67-t0uhyTed4$kNo%uu8r))?6vW;lWz@S%Ih#S|?0?;b^qdqdP}t0NI=@b<07 zVUB~e{wkMSPdU6@Mjh`J=(4*H^kIobA0g~Fpw2t^cF}DLm!e2%N?DzF*u;WJ^+Er! zegF>;Cdaiu4yxYROZ%$?U;hGws-mhoyc?d<*po)F&j=CSw13Fq5O$P#AQ5?Cduj__ zgC*q}hbrFvkU@D{Ma(c{s?1itmC45RW6p4`NYNgC%}kuhIlJShXnjjeS5wUl$2%NA zTJn{xl?hEdf%=9Y^O(GO(;k8Y9D+iZ*GI_F5fM~ydu*%bm7~)Jya?!O8EBTPJB0rq De~rmR diff --git a/1.19.2/README.md b/1.19.2/README.md index 49c0699..4b18d17 100644 --- a/1.19.2/README.md +++ b/1.19.2/README.md @@ -15,7 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.19.2/build.gradle b/1.19.2/build.gradle index f099442..1c776ad 100644 --- a/1.19.2/build.gradle +++ b/1.19.2/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.19.2/gradle.properties b/1.19.2/gradle.properties index 2b87c39..d681dac 100644 --- a/1.19.2/gradle.properties +++ b/1.19.2/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod diff --git a/1.19.3/.jenkins/Jenkinsfile.snapshot b/1.19.3/.jenkins/Jenkinsfile.snapshot index 00ddd21..1f67c23 100644 --- a/1.19.3/.jenkins/Jenkinsfile.snapshot +++ b/1.19.3/.jenkins/Jenkinsfile.snapshot @@ -3,7 +3,7 @@ def projectIcon = "https://cdn.modrinth.com/data/Nn8Wasaq/a172c634683a11a2e9ae59 def JDK = "17"; def majorMc = "1.19.3"; def modLoaders = "forge|fabric|quilt"; -def supportedMc = "1.19.3|1.19.4"; +def supportedMc = "1.19.4"; def reltype = "snapshot"; pipeline { 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 d0e31a6..4f0d773 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -43,11 +43,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -58,7 +58,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 c284781..55b8ba1 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable> cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); + } + } + +} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index b14e484..d50bf58 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..ae6f773 --- /dev/null +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final ServerStatus.Favicon internal; + + public WrappedFavicon(byte[] iconBytes) { + internal = new ServerStatus.Favicon(iconBytes); + } + + @ApiStatus.Internal + public WrappedFavicon(ServerStatus.Favicon internal) { + this.internal = internal; + } + + public byte[] iconBytes() { + return internal.iconBytes(); + } + + public ServerStatus.Favicon 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index af9736d..5759fde 100644 --- a/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -92,4 +92,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.19.3/Common/src/main/resources/craterlib.mixins.json b/1.19.3/Common/src/main/resources/craterlib.mixins.json index 1a739db..c910ed2 100644 --- a/1.19.3/Common/src/main/resources/craterlib.mixins.json +++ b/1.19.3/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,8 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.19.3/Fabric/build.gradle b/1.19.3/Fabric/build.gradle index 0641b33..bfa9be2 100644 --- a/1.19.3/Fabric/build.gradle +++ b/1.19.3/Fabric/build.gradle @@ -113,8 +113,8 @@ publisher { setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[FABRIC/QUILT 1.19.3/1.19.4] CraterLib - ${project.version}") - setGameVersions("1.19.3", "1.19.4") + setDisplayName("[FABRIC/QUILT 1.19.4] CraterLib - ${project.version}") + setGameVersions("1.19.4") setLoaders("fabric", "quilt") setArtifact(remapJar) setCurseEnvironment("both") diff --git a/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,52 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.19.3/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.19.3/Fabric/src/main/resources/craterlib.fabric.mixins.json index 7c59043..a6d0bc1 100644 --- a/1.19.3/Fabric/src/main/resources/craterlib.fabric.mixins.json +++ b/1.19.3/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -9,7 +9,8 @@ "TutorialMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.19.3/Fabric/src/main/resources/fabric.mod.json b/1.19.3/Fabric/src/main/resources/fabric.mod.json index 1f3ba7c..4ff5872 100644 --- a/1.19.3/Fabric/src/main/resources/fabric.mod.json +++ b/1.19.3/Fabric/src/main/resources/fabric.mod.json @@ -33,7 +33,7 @@ "depends": { "fabricloader": ">=0.15.0", "fabric-api": "*", - "minecraft": ">=1.19.3", + "minecraft": ">=1.19.4", "java": ">=17" } } diff --git a/1.19.3/Forge/build.gradle b/1.19.3/Forge/build.gradle index 76719c8..59bf756 100644 --- a/1.19.3/Forge/build.gradle +++ b/1.19.3/Forge/build.gradle @@ -107,8 +107,8 @@ publisher { setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[Forge 1.19.3/1.19.4] CraterLib - ${project.version}") - setGameVersions("1.19.3", "1.19.4") + setDisplayName("[Forge 1.19.4] CraterLib - ${project.version}") + setGameVersions("1.19.4") setLoaders("forge") setArtifact(remapJar) setCurseEnvironment("both") diff --git a/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 927bd23..78fb62b 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,6 +1,7 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; @@ -28,9 +29,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..969b05d --- /dev/null +++ b/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.forgeData() + ) + )); + } + } + +} diff --git a/1.19.3/Forge/src/main/resources/META-INF/mods.toml b/1.19.3/Forge/src/main/resources/META-INF/mods.toml index e6ebdd7..3d712a0 100644 --- a/1.19.3/Forge/src/main/resources/META-INF/mods.toml +++ b/1.19.3/Forge/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[44,)" +loaderVersion = "[45,)" license = "MIT" issueTrackerURL = "https://github.com/firstdarkdev/craterLib/issues" @@ -19,13 +19,13 @@ issueTrackerURL = "https://github.com/firstdarkdev/craterLib/issues" [[dependencies.${ mod_id }]] modId = "forge" mandatory = true - versionRange = "[44,)" + versionRange = "[45,)" ordering = "NONE" side = "BOTH" [[dependencies.${ mod_id }]] modId = "minecraft" mandatory = true - versionRange = "[1.19.3,1.20)" + versionRange = "[1.19.4,1.20)" ordering = "NONE" side = "BOTH" diff --git a/1.19.3/Forge/src/main/resources/craterlib.forge.mixins.json b/1.19.3/Forge/src/main/resources/craterlib.forge.mixins.json index aa072d1..892f6d7 100644 --- a/1.19.3/Forge/src/main/resources/craterlib.forge.mixins.json +++ b/1.19.3/Forge/src/main/resources/craterlib.forge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.19.3/NeoForge/src/main/resources/craterlib_logo.png b/1.19.3/NeoForge/src/main/resources/craterlib_logo.png deleted file mode 100644 index ce0159cc4aa4d823ea354042e531b4522d6dfead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49343 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 z 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 De~rmR diff --git a/1.19.3/README.md b/1.19.3/README.md index 1e3cffb..5cf4e46 100644 --- a/1.19.3/README.md +++ b/1.19.3/README.md @@ -15,8 +15,8 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | + - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.19.3/build.gradle b/1.19.3/build.gradle index f099442..1c776ad 100644 --- a/1.19.3/build.gradle +++ b/1.19.3/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.19.3/gradle.properties b/1.19.3/gradle.properties index e7a3770..48d04f3 100644 --- a/1.19.3/gradle.properties +++ b/1.19.3/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod @@ -10,15 +10,15 @@ mod_id=craterlib mod_name=CraterLib # Shared -minecraft_version=1.19.3 +minecraft_version=1.19.4 project_group=com.hypherionmc.craterlib # Fabric fabric_loader=0.15.11 -fabric_api=0.76.1+1.19.3 +fabric_api=0.87.2+1.19.4 # Forge -forge_version=44.1.0 +forge_version=45.3.0 # Dependencies moon_config=1.0.9 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 81a9ed7..1132e07 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -44,11 +44,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -59,7 +59,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 9426ccf..056f32c 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable> cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); + } + } + +} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index 5a15402..5529824 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(() -> ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..ae6f773 --- /dev/null +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final ServerStatus.Favicon internal; + + public WrappedFavicon(byte[] iconBytes) { + internal = new ServerStatus.Favicon(iconBytes); + } + + @ApiStatus.Internal + public WrappedFavicon(ServerStatus.Favicon internal) { + this.internal = internal; + } + + public byte[] iconBytes() { + return internal.iconBytes(); + } + + public ServerStatus.Favicon 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index af9736d..5759fde 100644 --- a/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.20.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -92,4 +92,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.20.2/Common/src/main/resources/craterlib.mixins.json b/1.20.2/Common/src/main/resources/craterlib.mixins.json index 1a739db..c910ed2 100644 --- a/1.20.2/Common/src/main/resources/craterlib.mixins.json +++ b/1.20.2/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,8 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index ec01614..7c644d4 100644 --- a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { - Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.20.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,52 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.20.2/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.20.2/Fabric/src/main/resources/craterlib.fabric.mixins.json index 7c59043..a6d0bc1 100644 --- a/1.20.2/Fabric/src/main/resources/craterlib.fabric.mixins.json +++ b/1.20.2/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -9,7 +9,8 @@ "TutorialMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 927bd23..78fb62b 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,6 +1,7 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; @@ -28,9 +29,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index c9b024b..8a0fcfb 100644 --- a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = component == null ? arg.decoratedContent() : component; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..969b05d --- /dev/null +++ b/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.forgeData() + ) + )); + } + } + +} diff --git a/1.20.2/Forge/src/main/resources/craterlib.forge.mixins.json b/1.20.2/Forge/src/main/resources/craterlib.forge.mixins.json index aa072d1..892f6d7 100644 --- a/1.20.2/Forge/src/main/resources/craterlib.forge.mixins.json +++ b/1.20.2/Forge/src/main/resources/craterlib.forge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.2/NeoForge/src/main/resources/craterlib_logo.png b/1.20.2/NeoForge/src/main/resources/craterlib_logo.png deleted file mode 100644 index ce0159cc4aa4d823ea354042e531b4522d6dfead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49343 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 z 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 De~rmR diff --git a/1.20.2/README.md b/1.20.2/README.md index 49c0699..4b18d17 100644 --- a/1.20.2/README.md +++ b/1.20.2/README.md @@ -15,7 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.20.2/build.gradle b/1.20.2/build.gradle index f099442..1c776ad 100644 --- a/1.20.2/build.gradle +++ b/1.20.2/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.20.2/gradle.properties b/1.20.2/gradle.properties index 46f844f..a9ef4cf 100644 --- a/1.20.2/gradle.properties +++ b/1.20.2/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 81a9ed7..1132e07 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -44,11 +44,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -59,7 +59,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 9426ccf..056f32c 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable> cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); + } + } + +} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index 5a15402..5529824 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(() -> ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..ae6f773 --- /dev/null +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final ServerStatus.Favicon internal; + + public WrappedFavicon(byte[] iconBytes) { + internal = new ServerStatus.Favicon(iconBytes); + } + + @ApiStatus.Internal + public WrappedFavicon(ServerStatus.Favicon internal) { + this.internal = internal; + } + + public byte[] iconBytes() { + return internal.iconBytes(); + } + + public ServerStatus.Favicon 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index af9736d..5759fde 100644 --- a/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.20.4/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -92,4 +92,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.20.4/Common/src/main/resources/craterlib.mixins.json b/1.20.4/Common/src/main/resources/craterlib.mixins.json index 1a739db..c910ed2 100644 --- a/1.20.4/Common/src/main/resources/craterlib.mixins.json +++ b/1.20.4/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,8 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index ec01614..7c644d4 100644 --- a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { - Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.20.4/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,52 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.20.4/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.20.4/Fabric/src/main/resources/craterlib.fabric.mixins.json index 7c59043..a6d0bc1 100644 --- a/1.20.4/Fabric/src/main/resources/craterlib.fabric.mixins.json +++ b/1.20.4/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -9,7 +9,8 @@ "TutorialMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 927bd23..d77853c 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,8 +1,8 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -28,9 +28,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index c9b024b..8a0fcfb 100644 --- a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = component == null ? arg.decoratedContent() : component; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..969b05d --- /dev/null +++ b/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.forgeData() + ) + )); + } + } + +} diff --git a/1.20.4/Forge/src/main/resources/craterlib.forge.mixins.json b/1.20.4/Forge/src/main/resources/craterlib.forge.mixins.json index aa072d1..892f6d7 100644 --- a/1.20.4/Forge/src/main/resources/craterlib.forge.mixins.json +++ b/1.20.4/Forge/src/main/resources/craterlib.forge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java index 91ca5b7..868b89f 100644 --- a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -36,8 +35,7 @@ public class NeoForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } diff --git a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java index 809671b..619565c 100644 --- a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -1,6 +1,7 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; @@ -28,9 +29,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index c9b024b..8a0fcfb 100644 --- a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = component == null ? arg.decoratedContent() : component; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..cb697db --- /dev/null +++ b/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.isModded() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json b/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json index aa072d1..892f6d7 100644 --- a/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ b/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.4/README.md b/1.20.4/README.md index 49c0699..4b18d17 100644 --- a/1.20.4/README.md +++ b/1.20.4/README.md @@ -15,7 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.20.4/build.gradle b/1.20.4/build.gradle index e7f9017..f37e586 100644 --- a/1.20.4/build.gradle +++ b/1.20.4/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.20.4/gradle.properties b/1.20.4/gradle.properties index 1b37337..792dfe7 100644 --- a/1.20.4/gradle.properties +++ b/1.20.4/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 50fb3cf..c4ea3a4 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -44,11 +44,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -59,7 +59,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 9426ccf..056f32c 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable> cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); + } + } + +} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index 5a15402..5529824 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(() -> ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..ae6f773 --- /dev/null +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final ServerStatus.Favicon internal; + + public WrappedFavicon(byte[] iconBytes) { + internal = new ServerStatus.Favicon(iconBytes); + } + + @ApiStatus.Internal + public WrappedFavicon(ServerStatus.Favicon internal) { + this.internal = internal; + } + + public byte[] iconBytes() { + return internal.iconBytes(); + } + + public ServerStatus.Favicon 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.20/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.20/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index af9736d..5759fde 100644 --- a/1.20/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.20/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -92,4 +92,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.20/Common/src/main/resources/craterlib.mixins.json b/1.20/Common/src/main/resources/craterlib.mixins.json index 1a739db..c910ed2 100644 --- a/1.20/Common/src/main/resources/craterlib.mixins.json +++ b/1.20/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,8 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,52 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.20/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.20/Fabric/src/main/resources/craterlib.fabric.mixins.json index 7c59043..a6d0bc1 100644 --- a/1.20/Fabric/src/main/resources/craterlib.fabric.mixins.json +++ b/1.20/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -9,7 +9,8 @@ "TutorialMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java index f354ddc..37ddf87 100644 --- a/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +++ b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; @@ -36,8 +35,7 @@ public class ForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } 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 927bd23..d77853c 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,8 +1,8 @@ package com.hypherionmc.craterlib.mixin; 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.NoConfigScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -28,9 +28,9 @@ public class ConfigScreenHandlerMixin { */ @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)) diff --git a/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..969b05d --- /dev/null +++ b/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.forgeData() + ) + )); + } + } + +} diff --git a/1.20/Forge/src/main/resources/craterlib.forge.mixins.json b/1.20/Forge/src/main/resources/craterlib.forge.mixins.json index aa072d1..892f6d7 100644 --- a/1.20/Forge/src/main/resources/craterlib.forge.mixins.json +++ b/1.20/Forge/src/main/resources/craterlib.forge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20/NeoForge/src/main/resources/craterlib_logo.png b/1.20/NeoForge/src/main/resources/craterlib_logo.png deleted file mode 100644 index ce0159cc4aa4d823ea354042e531b4522d6dfead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49343 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 z 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 De~rmR diff --git a/1.20/README.md b/1.20/README.md index 49c0699..4b18d17 100644 --- a/1.20/README.md +++ b/1.20/README.md @@ -15,7 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.21 | 🚧 | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.20/build.gradle b/1.20/build.gradle index 5d2a5e0..24084aa 100644 --- a/1.20/build.gradle +++ b/1.20/build.gradle @@ -56,11 +56,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.20/gradle.properties b/1.20/gradle.properties index 79b3647..8221d09 100644 --- a/1.20/gradle.properties +++ b/1.20/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod 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 cc4969e..35f29f0 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,53 +1,155 @@ package com.hypherionmc.craterlib.api.commands; +import com.hypherionmc.craterlib.compat.LuckPermsCompat; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.brigadier.arguments.ArgumentType; -import lombok.Getter; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; -import org.apache.commons.lang3.tuple.Pair; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; -@Getter public class CraterCommand { - private final HashMap, TriConsumer>> arguments = new LinkedHashMap<>(); - private Consumer executor; + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; - private final String commandName; - private int permissionLevel = 4; - - CraterCommand(String commandName) { - this.commandName = commandName; + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; } public static CraterCommand literal(String commandName) { - return new CraterCommand(commandName); + return new CraterCommand(Commands.literal(commandName)); } public CraterCommand requiresPermission(int perm) { - this.permissionLevel = perm; + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); return this; } - public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { - arguments.put(key, Pair.of(GameProfileArgument.gameProfile(), executor)); + public CraterCommand withNode(String key) { + this.luckPermNode = key; return this; } + public CraterCommand then(CraterCommand child) { + this.mojangCommand.then(child.mojangCommand); + return this; + } + + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); + return this; + } + + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( + BridgedPlayer.of(context.getSource().getPlayer()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); + return this; + } + + public CraterCommand execute(SingleCommandExecutor executor) { + this.mojangCommand.executes(context -> executor.run(BridgedCommandSourceStack.of(context.getSource()))); + return this; + } + + @Deprecated(forRemoval = true) public CraterCommand executes(Consumer ctx) { - executor = ctx; - return this; + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); } - public boolean hasArguments() { - return !arguments.isEmpty(); + @Deprecated(forRemoval = true) + public CraterCommand withGameProfileArgument(String key, TriConsumer, BridgedCommandSourceStack> executor) { + return this.withGameProfilesArgument(key, (player, argument, stack) -> { + executor.accept(player, argument, stack); + return 1; + }); } + @ApiStatus.Internal + public void register(CommandDispatcher stack) { + stack.register(this.mojangCommand); + } + + private boolean checkPermission(CommandSourceStack stack) { + 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); + } + + @FunctionalInterface + public interface CommandExecutorWithArgs { + int run(BridgedPlayer player, S argument, BridgedCommandSourceStack stack); + } + + @FunctionalInterface + public interface SingleCommandExecutor { + int run(S stack); + } } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java index 94be675..9e7d7a2 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -4,7 +4,6 @@ import com.hypherionmc.craterlib.core.event.CraterEvent; import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; @RequiredArgsConstructor @Getter diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java index 269065a..5adac03 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -2,14 +2,17 @@ package com.hypherionmc.craterlib.api.events.server; import com.hypherionmc.craterlib.api.commands.CraterCommand; import com.hypherionmc.craterlib.core.event.CraterEvent; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; -@NoArgsConstructor +@AllArgsConstructor public class CraterRegisterCommandEvent extends CraterEvent { + private final CommandDispatcher stack; + public void registerCommand(CraterCommand cmd) { - CommandsRegistry.INSTANCE.registerCommand(cmd); + cmd.register(stack); } } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ServerStatusEvent { + + @RequiredArgsConstructor + @Getter + @Setter + public static class StatusRequestEvent extends CraterEvent { + + private final Component status; + @Nullable + private Component newStatus = null; + + } + + @RequiredArgsConstructor + @Getter + @Setter + public static class FaviconRequestEvent extends CraterEvent { + + private final Optional favicon; + private Optional newIcon = Optional.empty(); + + } +} 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 c9e70ca..4072ea9 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 @@ -2,7 +2,7 @@ package com.hypherionmc.craterlib.client.gui.config; import com.hypherionmc.craterlib.CraterConstants; import com.hypherionmc.craterlib.client.gui.config.widgets.*; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +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; @@ -44,11 +44,11 @@ public class CraterConfigScreen extends Screen { private static final int BOTTOM = 24; private final Screen parent; private final List> options = new ArrayList<>(); - private final ModuleConfig config; + private final AbstractConfig config; public double scrollerAmount; private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; this.config = config; @@ -59,7 +59,7 @@ public class CraterConfigScreen extends Screen { } } - public CraterConfigScreen(ModuleConfig config, Screen parent) { + public CraterConfigScreen(AbstractConfig config, Screen parent) { this(config, parent, null); } 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 9426ccf..056f32c 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 @@ -1,7 +1,7 @@ package com.hypherionmc.craterlib.client.gui.config.widgets; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; -import com.hypherionmc.craterlib.core.config.ModuleConfig; +import com.hypherionmc.craterlib.core.config.AbstractConfig; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -15,10 +15,10 @@ import net.minecraft.network.chat.Component; public class SubConfigWidget extends AbstractConfigWidget { private final Object subConfig; - private final ModuleConfig config; + private final AbstractConfig config; private final Screen screen; - public SubConfigWidget(ModuleConfig config, Screen screen, Object subConfig) { + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { this.config = config; this.subConfig = subConfig; this.screen = screen; diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.compat; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.minecraft.server.level.ServerPlayer; + +public class LuckPermsCompat { + + public static final LuckPermsCompat INSTANCE = new LuckPermsCompat(); + private final LuckPerms luckPerms = LuckPermsProvider.get(); + + LuckPermsCompat() {} + + public boolean hasPermission(ServerPlayer player, String perm) { + User luckPermsUser = luckPerms.getPlayerAdapter(ServerPlayer.class).getUser(player); + return luckPermsUser.getCachedData().getPermissionData().checkPermission(perm).asBoolean(); + } + +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.AbstractConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.JsonConfigFormat; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import lombok.Getter; +import lombok.Setter; +import me.hypherionmc.moonconfig.core.Config; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +public abstract class AbstractConfig { + + /* Final Variables */ + private final transient File configPath; + private final transient String networkID; + private final transient String configName; + private final transient String modId; + private transient boolean wasSaveCalled = false; + + @Setter + private transient AbstractConfigFormat configFormat; + + public AbstractConfig(String modId, String configName) { + this(modId, null, configName); + } + + public AbstractConfig(String modId, @Nullable String subFolder, String configName) { + Config.setInsertionOrderPreserved(true); + + if (!configName.endsWith(".toml") && !configName.endsWith(".json")) + configName = configName + ".toml"; + + File configDir = new File("config" + (subFolder == null ? "" : File.separator + subFolder)); + configPath = new File(configDir, configName); + this.modId = modId; + this.networkID = modId + ":conf_" + configName.replace(".toml", "").replace(".json", "").replace("-", "_").toLowerCase(); + this.configName = configName.replace(".toml", "").replace(".json", ""); + configDir.mkdirs(); + + configFormat = configName.endsWith(".json") ? new JsonConfigFormat<>(configPath, this::onSave) : new TomlConfigFormat<>(configPath, this::onSave); + } + + public void registerAndSetup(S config) { + configFormat.register(config); + ConfigController.register_config(this); + this.configReloaded(); + } + + public void saveConfig(S config) { + this.wasSaveCalled = true; + configFormat.saveConfig(config); + } + + private void onSave() { + this.configReloaded(); + this.wasSaveCalled = false; + } + + public S readConfig(S config) { + return configFormat.readConfig(config); + } + + public void migrateConfig(S config) { + configFormat.migrateConfig(config); + } + + public abstract void configReloaded(); +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java index 41c9471..760ca93 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -3,6 +3,7 @@ package com.hypherionmc.craterlib.core.config; import com.hypherionmc.craterlib.CraterConstants; import lombok.Getter; import me.hypherionmc.moonconfig.core.file.FileWatcher; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.ApiStatus; import java.io.Serializable; @@ -18,7 +19,7 @@ public final class ConfigController implements Serializable { * Cache of registered configs */ @Getter - private static final HashMap monitoredConfigs = new HashMap<>(); + private static final HashMap> watchedConfigs = new HashMap<>(); /** * INTERNAL METHOD - Register and watch the config @@ -26,23 +27,34 @@ public final class ConfigController implements Serializable { * @param config - The config class to register and watch */ @ApiStatus.Internal + @Deprecated public static void register_config(ModuleConfig config) { - if (monitoredConfigs.containsKey(config)) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + ". Config already registered"); + register_config((AbstractConfig) config); + } + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + public static void register_config(AbstractConfig config) { + if (watchedConfigs.containsKey(config.getConfigPath().toString())) { + CraterConstants.LOG.error("Failed to register {}. Config already registered", config.getConfigPath().getName()); } else { FileWatcher configWatcher = new FileWatcher(); try { configWatcher.setWatch(config.getConfigPath(), () -> { - if (!config.isSaveCalled()) { - CraterConstants.LOG.info("Sending Reload Event for: " + config.getConfigPath().getName()); + if (!config.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); config.configReloaded(); } }); } catch (Exception e) { - CraterConstants.LOG.error("Failed to register " + config.getConfigPath().getName() + " for auto reloading. " + e.getMessage()); + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); } - monitoredConfigs.put(config, configWatcher); - CraterConstants.LOG.info("Registered " + config.getConfigPath().getName() + " successfully!"); + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); } } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java index 181efdc..e4850bf 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -1,9 +1,7 @@ package com.hypherionmc.craterlib.core.config; +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; import me.hypherionmc.moonconfig.core.CommentedConfig; -import me.hypherionmc.moonconfig.core.Config; -import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; -import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; import java.io.File; @@ -12,17 +10,8 @@ import java.io.File; * Base Config class containing the save, upgrading and loading logic. * All config classes must extend this class */ -public class ModuleConfig { - - /* Final Variables */ - private final transient File configPath; - private final transient String networkID; - - private final transient String configName; - - private final transient String modId; - - private transient boolean isSaveCalled = false; +@Deprecated +public class ModuleConfig extends AbstractConfig { /** * Set up the config @@ -35,20 +24,7 @@ public class ModuleConfig { } public ModuleConfig(String modId, String subFolder, String configName) { - /* Preserve the order of the config values */ - Config.setInsertionOrderPreserved(true); - - /* Configure Paths and Network SYNC ID */ - File configDir = new File("config" + (subFolder.isEmpty() ? "" : File.separator + subFolder)); - configPath = new File(configDir + File.separator + configName + ".toml"); - networkID = modId + ":conf_" + configName.replace("-", "_"); - this.modId = modId; - this.configName = configName; - - /* Check if the required directories exists, otherwise we create them */ - if (!configDir.exists()) { - configDir.mkdirs(); - } + super(modId, subFolder.isEmpty() ? null : subFolder, configName); } /** @@ -57,14 +33,7 @@ public class ModuleConfig { * @param config - The config class to use */ public void registerAndSetup(ModuleConfig config) { - if (!configPath.exists() || configPath.length() < 2) { - saveConfig(config); - } else { - migrateConfig(config); - } - /* Register the Config for Watching and events */ - ConfigController.register_config(this); - this.configReloaded(); + super.registerAndSetup(config); } /** @@ -73,16 +42,7 @@ public class ModuleConfig { * @param conf - The config class to serialize and save */ public void saveConfig(ModuleConfig conf) { - this.isSaveCalled = true; - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - - /* Save the config and fire the reload events */ - converter.toConfig(conf, config); - config.save(); - configReloaded(); - this.isSaveCalled = false; + super.registerAndSetup(conf); } /** @@ -92,14 +52,7 @@ public class ModuleConfig { * @return - Returns the loaded version of the class */ public T loadConfig(Object conf) { - /* Set up the Serializer and Config Object */ - ObjectConverter converter = new ObjectConverter(); - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Load the config and return the loaded config */ - converter.toObject(config, conf); - return (T) conf; + return (T) super.readConfig(conf); } /** @@ -108,31 +61,13 @@ public class ModuleConfig { * @param conf - The config class to load */ public void migrateConfig(ModuleConfig conf) { - /* Set up the Serializer and Config Objects */ - CommentedFileConfig config = CommentedFileConfig.builder(configPath).build(); - CommentedFileConfig newConfig = CommentedFileConfig.builder(configPath).build(); - config.load(); - - /* Upgrade the config */ - new ObjectConverter().toConfig(conf, newConfig); - updateConfigValues(config, newConfig, newConfig, ""); - newConfig.save(); - - config.close(); - newConfig.close(); + super.migrateConfig(conf); } public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { - /* Loop over the config keys and check what has changed */ - newConfig.valueMap().forEach((key, value) -> { - String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; - if (value instanceof CommentedConfig commentedConfig) { - updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); - } else { - outputConfig.set(finalKey, - oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); - } - }); + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } } /** @@ -141,7 +76,7 @@ public class ModuleConfig { * @return - The FILE object containing the config file */ public File getConfigPath() { - return configPath; + return super.getConfigPath(); } /** @@ -150,12 +85,13 @@ public class ModuleConfig { * @return - Returns the Sync ID in format modid:config_name */ public String getNetworkID() { - return networkID; + return super.getNetworkID(); } /** * Fired whenever changes to the config are detected */ + @Override public void configReloaded() { } @@ -166,7 +102,7 @@ public class ModuleConfig { * @return */ public String getConfigName() { - return configName; + return super.getConfigName(); } /** @@ -175,10 +111,10 @@ public class ModuleConfig { * @return */ public String getModId() { - return modId; + return super.getModId(); } public boolean isSaveCalled() { - return isSaveCalled; + return super.isWasSaveCalled(); } } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java @@ -0,0 +1,32 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.hypherionmc.moonconfig.core.Config; + +import java.io.File; + +@RequiredArgsConstructor +@Getter +public abstract class AbstractConfigFormat { + + private final File configPath; + private final Runnable onSave; + + public void register(S conf) { + if (!configPath.exists() || configPath.length() < 2) { + saveConfig(conf); + } else { + migrateConfig(conf); + } + } + + public boolean wasConfigChanged(Config old, Config newConfig) { + return true; + } + + public abstract void saveConfig(S config); + public abstract S readConfig(S config); + public abstract void migrateConfig(S config); + +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..0db35fa --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.Config; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.FileConfig; + +import java.io.File; + +public class JsonConfigFormat extends AbstractConfigFormat { + + public JsonConfigFormat(File configPath, Runnable afterSave) { + super(configPath, afterSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + FileConfig config = FileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + FileConfig config = FileConfig.builder(getConfigPath()).build(); + FileConfig newConfig = FileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(Config oldConfig, Config newConfig, Config outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof Config commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..e3f9763 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java @@ -0,0 +1,67 @@ +package com.hypherionmc.craterlib.core.config.formats; + +import me.hypherionmc.moonconfig.core.CommentedConfig; +import me.hypherionmc.moonconfig.core.conversion.ObjectConverter; +import me.hypherionmc.moonconfig.core.file.CommentedFileConfig; + +import java.io.File; + +public class TomlConfigFormat extends AbstractConfigFormat { + + public TomlConfigFormat(File configPath, Runnable onSave) { + super(configPath, onSave); + } + + @Override + public void saveConfig(S conf) { + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); + + converter.toConfig(conf, config); + config.save(); + getOnSave().run(); + } + + @Override + public S readConfig(S conf) { + /* Set up the Serializer and Config Object */ + ObjectConverter converter = new ObjectConverter(); + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + config.load(); + + /* Load the config and return the loaded config */ + converter.toObject(config, conf); + return conf; + } + + @Override + public void migrateConfig(S conf) { + /* Set up the Serializer and Config Objects */ + CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).build(); + CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); + config.load(); + + /* Upgrade the config */ + if (wasConfigChanged(config, newConfig)) { + new ObjectConverter().toConfig(conf, newConfig); + updateConfigValues(config, newConfig, newConfig, ""); + newConfig.save(); + } + + config.close(); + newConfig.close(); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + /* Loop over the config keys and check what has changed */ + newConfig.valueMap().forEach((key, value) -> { + String finalKey = subKey + (subKey.isEmpty() ? "" : ".") + key; + if (value instanceof CommentedConfig commentedConfig) { + updateConfigValues(oldConfig, commentedConfig, outputConfig, finalKey); + } else { + outputConfig.set(finalKey, + oldConfig.contains(finalKey) ? oldConfig.get(finalKey) : value); + } + }); + } +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.network.protocol.status.WrappedServerStatus; +import net.minecraft.network.protocol.status.ServerStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; + +@Mixin(ServerStatus.class) +public class ServerStatusMixin { + + @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) + private void injectIconEvent(CallbackInfoReturnable> cir) { + ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { + cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); + } + } + +} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java index 5a15402..5529824 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -16,6 +16,10 @@ public class BridgedCommandSourceStack { internal.sendSuccess(() -> ChatUtils.adventureToMojang(supplier.get()), bl); } + public void sendFailure(Component text) { + internal.sendFailure(ChatUtils.adventureToMojang(text)); + } + public CommandSourceStack toMojang() { return internal; } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java deleted file mode 100644 index 37ad15b..0000000 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.hypherionmc.craterlib.nojang.commands; - -import com.hypherionmc.craterlib.api.commands.CraterCommand; -import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; -import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; -import com.hypherionmc.craterlib.utils.TriConsumer; -import com.mojang.authlib.GameProfile; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.GameProfileArgument; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CommandsRegistry { - - public static final CommandsRegistry INSTANCE = new CommandsRegistry(); - - private final List commands = new ArrayList<>(); - - public void registerCommand(CraterCommand cmd) { - commands.add(cmd); - } - - public void registerCommands(CommandDispatcher stack) { - commands.forEach(cmd -> { - if (cmd.hasArguments()) { - CommandWithArguments.register(cmd, stack); - } else { - CommandWithoutArguments.register(cmd, stack); - } - }); - } - - static class CommandWithoutArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())) - .executes(context -> { - cmd.getExecutor().accept(BridgedCommandSourceStack.of(context.getSource())); - return 1; - }); - - dispatcher.register(command); - } - - } - - @SuppressWarnings("unchecked") - static class CommandWithArguments { - - public static void register(CraterCommand cmd, CommandDispatcher dispatcher) { - LiteralArgumentBuilder command = Commands.literal(cmd.getCommandName()) - .requires(source -> source.hasPermission(cmd.getPermissionLevel())); - - cmd.getArguments().forEach((key, pair) -> command.then(Commands.argument(key, pair.getLeft()).executes(context -> { - - // This is FUCKING UGLY.... Need to improve this in the future - if (pair.getLeft() instanceof GameProfileArgument) { - Collection profiles = GameProfileArgument.getGameProfiles(context, key); - List bridgedGameProfiles = new ArrayList<>(); - - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) - .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - - return 1; - }))); - - dispatcher.register(command); - } - - } - -} diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java new file mode 100644 index 0000000..ae6f773 --- /dev/null +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.nojang.network.protocol.status; + +import net.minecraft.network.protocol.status.ServerStatus; +import org.jetbrains.annotations.ApiStatus; + +public final class WrappedServerStatus { + + public static final class WrappedFavicon { + + private final ServerStatus.Favicon internal; + + public WrappedFavicon(byte[] iconBytes) { + internal = new ServerStatus.Favicon(iconBytes); + } + + @ApiStatus.Internal + public WrappedFavicon(ServerStatus.Favicon internal) { + this.internal = internal; + } + + public byte[] iconBytes() { + return internal.iconBytes(); + } + + public ServerStatus.Favicon 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 8aa6d49..2241836 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 @@ -57,6 +57,11 @@ public class BridgedPlayer { return null; } + public void disconnect(Component message) { + if (isServerPlayer()) + toMojangServerPlayer().connection.disconnect(ChatUtils.adventureToMojang(message)); + } + public ServerPlayer toMojangServerPlayer() { return (ServerPlayer) internal; } diff --git a/1.21/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.21/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java index 02ddae4..c09982c 100644 --- a/1.21/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java +++ b/1.21/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -108,4 +108,11 @@ public class ChatUtils { return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); } + public static net.kyori.adventure.text.Component format(String value) { + return net.kyori.adventure.text.Component.translatable(convertFormattingCodes(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } } diff --git a/1.21/Common/src/main/resources/craterlib.mixins.json b/1.21/Common/src/main/resources/craterlib.mixins.json index 1a739db..c910ed2 100644 --- a/1.21/Common/src/main/resources/craterlib.mixins.json +++ b/1.21/Common/src/main/resources/craterlib.mixins.json @@ -16,7 +16,8 @@ "events.CommandMixin", "events.PlayerAdvancementsMixin", "events.PlayerListMixin", - "events.ServerPlayerMixin" + "events.ServerPlayerMixin", + "events.ServerStatusMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.21/Fabric/build.gradle b/1.21/Fabric/build.gradle index d3a187f..0fd9480 100644 --- a/1.21/Fabric/build.gradle +++ b/1.21/Fabric/build.gradle @@ -113,8 +113,8 @@ publisher { setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") - setGameVersions("1.21") + setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") + setGameVersions("1.21", "1.21.1") setLoaders("fabric", "quilt") setArtifact(remapJar) setCurseEnvironment("both") diff --git a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java index c66c210..5e93a3a 100644 --- a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -9,7 +9,6 @@ import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; import com.hypherionmc.craterlib.core.networking.data.PacketSide; import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -21,8 +20,7 @@ public class CraterLibInitializer implements ModInitializer { public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); 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 6615352..5b34ff1 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 @@ -2,7 +2,6 @@ package com.hypherionmc.craterlib; import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; import com.hypherionmc.craterlib.core.config.ConfigController; -import com.hypherionmc.craterlib.core.config.ModuleConfig; import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; @@ -19,9 +18,9 @@ public class CraterLibModMenuIntegration implements ModMenuApi { public Map> getProvidedConfigScreenFactories() { Map> configScreens = new HashMap<>(); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - configScreens.put(((ModuleConfig) conf).getModId(), screen -> new CraterConfigScreen((ModuleConfig) conf, screen)); + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); } }); diff --git a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index 91b157b..576a276 100644 --- a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { - Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.21/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,52 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.21/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.21/Fabric/src/main/resources/craterlib.fabric.mixins.json index 7c59043..a6d0bc1 100644 --- a/1.21/Fabric/src/main/resources/craterlib.fabric.mixins.json +++ b/1.21/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -9,7 +9,8 @@ "TutorialMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index 7adbbe1..012b6ff 100644 --- a/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = component == null ? arg.decoratedContent() : component; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..cb697db --- /dev/null +++ b/1.21/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.isModded() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.21/Forge/src/main/resources/craterlib.forge.mixins.json b/1.21/Forge/src/main/resources/craterlib.forge.mixins.json index aa072d1..892f6d7 100644 --- a/1.21/Forge/src/main/resources/craterlib.forge.mixins.json +++ b/1.21/Forge/src/main/resources/craterlib.forge.mixins.json @@ -9,7 +9,8 @@ "ConfigScreenHandlerMixin" ], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.21/NeoForge/build.gradle b/1.21/NeoForge/build.gradle index b3b5659..f6ef5c7 100644 --- a/1.21/NeoForge/build.gradle +++ b/1.21/NeoForge/build.gradle @@ -106,8 +106,8 @@ publisher { setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") - setGameVersions("1.21") + setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") + setGameVersions("1.21", "1.21.1") setLoaders("neoforge") setArtifact(remapJar) setCurseEnvironment("both") 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 5aeb1fd..e7d9013 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 @@ -2,8 +2,8 @@ package com.hypherionmc.craterlib.client; import com.hypherionmc.craterlib.api.events.client.LateInitEvent; 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.NoConfigScreen; import com.hypherionmc.craterlib.core.event.CraterEventBus; import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -51,9 +51,9 @@ public class NeoForgeClientHelper implements ClientPlatform { LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); CraterEventBus.INSTANCE.postEvent(event); - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { - ModuleConfig config = (ModuleConfig) conf; + AbstractConfig config = watcher.getLeft(); ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); } }); diff --git a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java index 91ca5b7..868b89f 100644 --- a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java @@ -3,7 +3,6 @@ package com.hypherionmc.craterlib.common; import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -36,8 +35,7 @@ public class NeoForgeServerEvents { @SubscribeEvent public void onCommandRegister(RegisterCommandsEvent event) { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); } } diff --git a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java index 7adbbe1..012b6ff 100644 --- a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java +++ b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -27,8 +27,8 @@ public class ServerGamePacketListenerImplMixin { cancellable = true ) private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); + Component finalcomp = component == null ? arg.decoratedContent() : component; + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) ci.cancel(); diff --git a/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..cb697db --- /dev/null +++ b/1.21/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +import net.minecraft.server.network.ServerStatusPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerStatusPacketListenerImpl.class) +public class ServerStatusPacketListenerMixin { + + @Shadow + @Final + private ServerStatus status; + + @Shadow @Final private Connection connection; + + @Inject(method = "handleStatusRequest", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.BEFORE), + cancellable = true + ) + private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { + ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewStatus() != null) { + ci.cancel(); + this.connection.send(new ClientboundStatusResponsePacket( + new ServerStatus(ChatUtils.adventureToMojang( + event.getNewStatus()), + status.players(), + status.version(), + status.favicon(), + status.enforcesSecureChat(), + status.isModded() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.21/NeoForge/src/main/resources/craterlib.neoforge.mixins.json b/1.21/NeoForge/src/main/resources/craterlib.neoforge.mixins.json index e1af981..147f7e0 100644 --- a/1.21/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ b/1.21/NeoForge/src/main/resources/craterlib.neoforge.mixins.json @@ -7,7 +7,8 @@ ], "client": [], "server": [ - "ServerGamePacketListenerImplMixin" + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.21/README.md b/1.21/README.md index 63c4aed..4b18d17 100644 --- a/1.21/README.md +++ b/1.21/README.md @@ -15,8 +15,7 @@ A Library mod and modding api for easier multi-version minecraft and mod loader | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | -| 1.21 | ✳️ | +| 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/1.21/build.gradle b/1.21/build.gradle index c4a979f..861a660 100644 --- a/1.21/build.gradle +++ b/1.21/build.gradle @@ -57,11 +57,13 @@ subprojects { // All Projects shade "me.hypherionmc.moon-config:core:${moon_config}" shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" shade "net.kyori:adventure-api:${adventure}" shade "net.kyori:adventure-text-serializer-gson:${adventure}" + compileOnly 'net.luckperms:api:5.4' compileOnly("org.projectlombok:lombok:${lombok}") annotationProcessor("org.projectlombok:lombok:${lombok}") } diff --git a/1.21/gradle.properties b/1.21/gradle.properties index f086dac..080e29d 100644 --- a/1.21/gradle.properties +++ b/1.21/gradle.properties @@ -1,7 +1,7 @@ #Project version_major=2 -version_minor=0 -version_patch=3 +version_minor=1 +version_patch=0 version_build=0 #Mod diff --git a/commit.sha b/commit.sha index 38347a2..646d8ca 100644 --- a/commit.sha +++ b/commit.sha @@ -1 +1 @@ -2c13d507c30a460256f006d30428d7ead5da2719 \ No newline at end of file +0dbf07de46f6156b78fe826379cde3739981546f \ 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 0a87493..4c6053c 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"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.18.2"; 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 new file mode 100644 index 0000000..2149294 --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java.patch @@ -0,0 +1,77 @@ +--- 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 @@ + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.commands.Commands; + import net.minecraft.commands.arguments.GameProfileArgument; ++import net.minecraft.server.level.ServerPlayer; + import net.minecraft.world.entity.player.Player; + import org.jetbrains.annotations.ApiStatus; + +@@ -53,7 +54,7 @@ + public CraterCommand withGameProfilesArgument(String key, CommandExecutorWithArgs> executor) { + this.mojangCommand.then(Commands.argument(key, GameProfileArgument.gameProfile()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + GameProfileArgument.getGameProfiles(context, key).stream().map(BridgedGameProfile::of).toList(), + BridgedCommandSourceStack.of(context.getSource())) + )); +@@ -63,7 +64,7 @@ + public CraterCommand withBoolArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, BoolArgumentType.bool()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + BoolArgumentType.getBool(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); +@@ -73,7 +74,7 @@ + public CraterCommand withWordArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.word()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); +@@ -83,7 +84,7 @@ + public CraterCommand withStringArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.string()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); +@@ -93,7 +94,7 @@ + public CraterCommand withPhraseArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, StringArgumentType.greedyString()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + StringArgumentType.getString(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); +@@ -103,7 +104,7 @@ + public CraterCommand withIntegerArgument(String key, CommandExecutorWithArgs executor) { + this.mojangCommand.then(Commands.argument(key, IntegerArgumentType.integer()) + .executes(context -> executor.run( +- BridgedPlayer.of(context.getSource().getPlayer()), ++ BridgedPlayer.of(context.getSource().getPlayerOrException()), + IntegerArgumentType.getInteger(context, key), + BridgedCommandSourceStack.of(context.getSource()) + ))); +@@ -137,10 +138,10 @@ + } + + private boolean checkPermission(CommandSourceStack stack) { +- 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 LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel); ++ return LuckPermsCompat.INSTANCE.hasPermission((ServerPlayer) stack.getEntity(), this.luckPermNode) || stack.hasPermission(this.permLevel); + } + + @FunctionalInterface 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 4f8722e..fcd7323 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 @@ -26,7 +26,7 @@ @@ -49,7 +50,7 @@ private boolean dragging; - public CraterConfigScreen(ModuleConfig config, Screen parent, Object subConfig) { + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { - super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); + super(new TranslatableComponent("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); this.parent = parent; 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 8c4deff..01902bf 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 @@ -3,7 +3,7 @@ @@ -2,12 +2,12 @@ import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; - import com.hypherionmc.craterlib.core.config.ModuleConfig; + import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch new file mode 100644 index 0000000..19f9b0f --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch @@ -0,0 +1,20 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java +@@ -14,13 +14,13 @@ + @Mixin(ServerStatus.class) + public class ServerStatusMixin { + +- @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) +- private void injectIconEvent(CallbackInfoReturnable> cir) { +- ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); ++ @Inject(method = "getFavicon", at = @At("RETURN"), cancellable = true) ++ private void injectIconEvent(CallbackInfoReturnable cir) { ++ ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { +- cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); ++ cir.setReturnValue(event.getNewIcon().get().toMojang()); + } + } + diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..0a78917 --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,49 @@ +--- /dev/null ++++ b/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java +@@ -1,0 +1,46 @@ ++package com.hypherionmc.craterlib.mixin.events; ++ ++import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; ++import com.hypherionmc.craterlib.core.event.CraterEventBus; ++import com.hypherionmc.craterlib.utils.ChatUtils; ++import net.minecraft.network.Connection; ++import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; ++import net.minecraft.network.protocol.status.ServerStatus; ++import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.network.ServerStatusPacketListenerImpl; ++import org.spongepowered.asm.mixin.Final; ++import org.spongepowered.asm.mixin.Mixin; ++import org.spongepowered.asm.mixin.Shadow; ++import org.spongepowered.asm.mixin.injection.At; ++import org.spongepowered.asm.mixin.injection.Inject; ++import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; ++ ++@Mixin(ServerStatusPacketListenerImpl.class) ++public class ServerStatusPacketListenerMixin { ++ ++ @Shadow @Final private Connection connection; ++ ++ @Shadow @Final private MinecraftServer server; ++ ++ @Inject(method = "handleStatusRequest", ++ at = @At( ++ value = "INVOKE", ++ target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", ++ shift = At.Shift.BEFORE), ++ cancellable = true ++ ) ++ private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { ++ ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(server.getStatus().getDescription())); ++ CraterEventBus.INSTANCE.postEvent(event); ++ ++ if (event.getNewStatus() != null) { ++ ci.cancel(); ++ ServerStatus serverStatus = this.server.getStatus(); ++ serverStatus.setDescription(ChatUtils.adventureToMojang(event.getNewStatus())); ++ ++ this.connection.send(new ClientboundStatusResponsePacket(serverStatus)); ++ } ++ } ++ ++} diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch index 2decb2b..bcb0d0f 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch @@ -8,4 +8,4 @@ + internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } - public CommandSourceStack toMojang() { + public void sendFailure(Component text) { diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java.patch deleted file mode 100644 index 2222357..0000000 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java -+++ b/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/CommandsRegistry.java -@@ -70,7 +70,7 @@ - profiles.forEach(p -> bridgedGameProfiles.add(BridgedGameProfile.of(p))); - - ((TriConsumer, BridgedCommandSourceStack>) pair.getRight()) -- .accept(BridgedPlayer.of(context.getSource().getPlayer()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); -+ .accept(BridgedPlayer.of(context.getSource().getPlayerOrException()), bridgedGameProfiles, BridgedCommandSourceStack.of(context.getSource())); - return 1; - } - diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch new file mode 100644 index 0000000..2149f59 --- /dev/null +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch @@ -0,0 +1,48 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java +@@ -1,28 +1,39 @@ + package com.hypherionmc.craterlib.nojang.network.protocol.status; + +-import net.minecraft.network.protocol.status.ServerStatus; + import org.jetbrains.annotations.ApiStatus; + ++import java.nio.charset.StandardCharsets; ++import java.util.Base64; ++ + public final class WrappedServerStatus { + + public static final class WrappedFavicon { + +- private final ServerStatus.Favicon internal; ++ private final String internal; ++ private final byte[] iconBytes; + + public WrappedFavicon(byte[] iconBytes) { +- internal = new ServerStatus.Favicon(iconBytes); ++ this.iconBytes = iconBytes; ++ ++ if (iconBytes != null) { ++ byte[] encoded = Base64.getEncoder().encode(iconBytes); ++ internal = "data:image/png;base64," + new String(encoded, StandardCharsets.UTF_8); ++ } else { ++ internal = null; ++ } + } + + @ApiStatus.Internal +- public WrappedFavicon(ServerStatus.Favicon internal) { ++ public WrappedFavicon(String internal) { + this.internal = internal; ++ this.iconBytes = new byte[0]; + } + + public byte[] iconBytes() { +- return internal.iconBytes(); ++ return iconBytes; + } + +- public ServerStatus.Favicon toMojang() { ++ public String toMojang() { + return internal; + } + diff --git a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java.patch b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java.patch index cd829ff..2c72ec3 100644 --- a/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java.patch +++ b/patches/1.18.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java.patch @@ -73,4 +73,4 @@ + return mojangToAdventure(new TranslatableComponent(Util.makeDescriptionId("biome", identifier.toMojang()))); } - } + public static net.kyori.adventure.text.Component format(String value) { diff --git a/patches/1.18.2/Common/src/main/resources/craterlib.mixins.json.patch b/patches/1.18.2/Common/src/main/resources/craterlib.mixins.json.patch new file mode 100644 index 0000000..30e41c2 --- /dev/null +++ b/patches/1.18.2/Common/src/main/resources/craterlib.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Common/src/main/resources/craterlib.mixins.json ++++ b/Common/src/main/resources/craterlib.mixins.json +@@ -17,7 +17,8 @@ + "events.PlayerAdvancementsMixin", + "events.PlayerListMixin", + "events.ServerPlayerMixin", +- "events.ServerStatusMixin" ++ "events.ServerStatusMixin", ++ "events.ServerStatusPacketListenerMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.18.2/Fabric/build.gradle.patch b/patches/1.18.2/Fabric/build.gradle.patch index 29cbdf2..19cd43d 100644 --- a/patches/1.18.2/Fabric/build.gradle.patch +++ b/patches/1.18.2/Fabric/build.gradle.patch @@ -4,8 +4,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + setDisplayName("[FABRIC/QUILT 1.18.2] CraterLib - ${project.version}") + setGameVersions("1.18.2") setLoaders("fabric", "quilt") diff --git a/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java.patch b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java.patch index bce76ef..2a761ad 100644 --- a/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java.patch +++ b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java.patch @@ -1,7 +1,7 @@ --- a/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java +++ b/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java -@@ -12,7 +12,7 @@ - import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; +@@ -11,7 +11,7 @@ + import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -9,12 +9,12 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; public class CraterLibInitializer implements ModInitializer { -@@ -20,7 +20,7 @@ +@@ -19,7 +19,7 @@ @Override public void onInitialize() { new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); - CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { - CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); - CommandsRegistry.INSTANCE.registerCommands(dispatcher); + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); }); + diff --git a/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 5378caa..01c6e16 100644 --- a/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -24,8 +24,8 @@ cancellable = true ) - private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { -- Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(TextFilter.FilteredText arg, CallbackInfo ci) { + Component message = new TextComponent(arg.getRaw()); + if (message.getString().startsWith("/")) diff --git a/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..3026880 --- /dev/null +++ b/patches/1.18.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,55 @@ +--- a/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,52 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.18.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch b/patches/1.18.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch new file mode 100644 index 0000000..8586f71 --- /dev/null +++ b/patches/1.18.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Fabric/src/main/resources/craterlib.fabric.mixins.json ++++ b/Fabric/src/main/resources/craterlib.fabric.mixins.json +@@ -9,8 +9,7 @@ + "TutorialMixin" + ], + "server": [ +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" ++ "ServerGamePacketListenerImplMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 972d75d..1f38ae1 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,6 +1,12 @@ --- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java +++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -6,7 +6,7 @@ +@@ -1,12 +1,12 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -9,7 +15,7 @@ import net.minecraftforge.forgespi.language.IModInfo; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -@@ -19,14 +19,14 @@ +@@ -19,18 +19,18 @@ /** * @author HypherionSA */ @@ -24,5 +30,11 @@ - @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) + @Inject(at = @At("RETURN"), method = "getGuiFactoryFor", cancellable = true, remap = false) private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { - ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- 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)) diff --git a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 4cf20de..d5d74d2 100644 --- a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -24,8 +24,8 @@ cancellable = true ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(TextFilter.FilteredText arg, CallbackInfo ci) { + Component message = new TextComponent(arg.getRaw()); + if (message.getString().startsWith("/")) diff --git a/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..e22d953 --- /dev/null +++ b/patches/1.18.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.18.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch b/patches/1.18.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch new file mode 100644 index 0000000..1c81cc5 --- /dev/null +++ b/patches/1.18.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Forge/src/main/resources/craterlib.forge.mixins.json ++++ b/Forge/src/main/resources/craterlib.forge.mixins.json +@@ -9,8 +9,7 @@ + "ConfigScreenHandlerMixin" + ], + "server": [ +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" ++ "ServerGamePacketListenerImplMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.18.2/NeoForge/build.gradle.patch b/patches/1.18.2/NeoForge/build.gradle.patch index 4a155d8..2ee2c57 100644 --- a/patches/1.18.2/NeoForge/build.gradle.patch +++ b/patches/1.18.2/NeoForge/build.gradle.patch @@ -109,8 +109,8 @@ - setVersionType("release") - setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") 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 a0d386b..6f63513 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 @@ -5,8 +5,8 @@ - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -54,9 +54,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch index 0dab07e..3176ade 100644 --- a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch +++ b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch @@ -1,12 +1,11 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,41 +1,0 @@ -package com.hypherionmc.craterlib.common; - -import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; -import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; -import com.hypherionmc.craterlib.core.event.CraterEventBus; --import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -39,8 +38,7 @@ - - @SubscribeEvent - public void onCommandRegister(RegisterCommandsEvent event) { -- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); -- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); - } - -} diff --git a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 631e6a6..2f716b8 100644 --- a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ - cancellable = true - ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); - CraterEventBus.INSTANCE.postEvent(event); - if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..555de60 --- /dev/null +++ b/patches/1.18.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.18.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.18.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index 3732d52..075f957 100644 --- a/patches/1.18.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.18.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -1,6 +1,6 @@ --- a/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ /dev/null -@@ -1,15 +1,0 @@ +@@ -1,16 +1,0 @@ -{ - "required": true, - "minVersion": "0.8", @@ -10,7 +10,8 @@ - ], - "client": [], - "server": [ -- "ServerGamePacketListenerImplMixin" +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" - ], - "injectors": { - "defaultRequire": 1 diff --git a/patches/1.18.2/README.md.patch b/patches/1.18.2/README.md.patch index 5fd4d92..4aea58e 100644 --- a/patches/1.18.2/README.md.patch +++ b/patches/1.18.2/README.md.patch @@ -1,12 +1,10 @@ --- a/README.md +++ b/README.md -@@ -15,8 +15,7 @@ +@@ -15,7 +15,6 @@ | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | --| 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.18.2/build.gradle.patch b/patches/1.18.2/build.gradle.patch index 879d507..890499e 100644 --- a/patches/1.18.2/build.gradle.patch +++ b/patches/1.18.2/build.gradle.patch @@ -11,9 +11,9 @@ group = rootProject.group -@@ -58,7 +58,7 @@ - shade "me.hypherionmc.moon-config:core:${moon_config}" +@@ -59,7 +59,7 @@ shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" - shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" + shade "me.hypherionmc.sdlink:mcdiscordformatter-1.18.1:${discord_formatter}" diff --git a/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.19.2/.jenkins/Jenkinsfile.snapshot.patch index 43a8aa4..2c388f3 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"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -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/widgets/SubConfigWidget.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java.patch index 608d425..4863fd7 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 @@ -3,7 +3,7 @@ @@ -2,9 +2,9 @@ import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; - import com.hypherionmc.craterlib.core.config.ModuleConfig; + import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch new file mode 100644 index 0000000..19f9b0f --- /dev/null +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java.patch @@ -0,0 +1,20 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java +@@ -14,13 +14,13 @@ + @Mixin(ServerStatus.class) + public class ServerStatusMixin { + +- @Inject(method = "favicon", at = @At("RETURN"), cancellable = true) +- private void injectIconEvent(CallbackInfoReturnable> cir) { +- ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue().get()))); ++ @Inject(method = "getFavicon", at = @At("RETURN"), cancellable = true) ++ private void injectIconEvent(CallbackInfoReturnable cir) { ++ ServerStatusEvent.FaviconRequestEvent event = new ServerStatusEvent.FaviconRequestEvent(cir.getReturnValue().isEmpty() ? Optional.empty() : Optional.of(new WrappedServerStatus.WrappedFavicon(cir.getReturnValue()))); + CraterEventBus.INSTANCE.postEvent(event); + + if (event.getNewIcon().isPresent()) { +- cir.setReturnValue(Optional.of(event.getNewIcon().get().toMojang())); ++ cir.setReturnValue(event.getNewIcon().get().toMojang()); + } + } + diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..0a78917 --- /dev/null +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,49 @@ +--- /dev/null ++++ b/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusPacketListenerMixin.java +@@ -1,0 +1,46 @@ ++package com.hypherionmc.craterlib.mixin.events; ++ ++import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; ++import com.hypherionmc.craterlib.core.event.CraterEventBus; ++import com.hypherionmc.craterlib.utils.ChatUtils; ++import net.minecraft.network.Connection; ++import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; ++import net.minecraft.network.protocol.status.ServerStatus; ++import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.network.ServerStatusPacketListenerImpl; ++import org.spongepowered.asm.mixin.Final; ++import org.spongepowered.asm.mixin.Mixin; ++import org.spongepowered.asm.mixin.Shadow; ++import org.spongepowered.asm.mixin.injection.At; ++import org.spongepowered.asm.mixin.injection.Inject; ++import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; ++ ++@Mixin(ServerStatusPacketListenerImpl.class) ++public class ServerStatusPacketListenerMixin { ++ ++ @Shadow @Final private Connection connection; ++ ++ @Shadow @Final private MinecraftServer server; ++ ++ @Inject(method = "handleStatusRequest", ++ at = @At( ++ value = "INVOKE", ++ target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", ++ shift = At.Shift.BEFORE), ++ cancellable = true ++ ) ++ private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { ++ ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(server.getStatus().getDescription())); ++ CraterEventBus.INSTANCE.postEvent(event); ++ ++ if (event.getNewStatus() != null) { ++ ci.cancel(); ++ ServerStatus serverStatus = this.server.getStatus(); ++ serverStatus.setDescription(ChatUtils.adventureToMojang(event.getNewStatus())); ++ ++ this.connection.send(new ClientboundStatusResponsePacket(serverStatus)); ++ } ++ } ++ ++} diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch index 2decb2b..bcb0d0f 100644 --- a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch @@ -8,4 +8,4 @@ + internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } - public CommandSourceStack toMojang() { + public void sendFailure(Component text) { diff --git a/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch new file mode 100644 index 0000000..ee6270f --- /dev/null +++ b/patches/1.19.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java.patch @@ -0,0 +1,46 @@ +--- a/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java ++++ b/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java +@@ -3,26 +3,38 @@ + import net.minecraft.network.protocol.status.ServerStatus; + import org.jetbrains.annotations.ApiStatus; + ++import java.nio.charset.StandardCharsets; ++import java.util.Base64; ++ + public final class WrappedServerStatus { + + public static final class WrappedFavicon { + +- private final ServerStatus.Favicon internal; ++ private final String internal; ++ private final byte[] iconBytes; + + public WrappedFavicon(byte[] iconBytes) { +- internal = new ServerStatus.Favicon(iconBytes); ++ this.iconBytes = iconBytes; ++ ++ if (iconBytes != null) { ++ byte[] encoded = Base64.getEncoder().encode(iconBytes); ++ internal = "data:image/png;base64," + new String(encoded, StandardCharsets.UTF_8); ++ } else { ++ internal = null; ++ } + } + + @ApiStatus.Internal +- public WrappedFavicon(ServerStatus.Favicon internal) { ++ public WrappedFavicon(String internal) { + this.internal = internal; ++ this.iconBytes = new byte[0]; + } + + public byte[] iconBytes() { +- return internal.iconBytes(); ++ return iconBytes; + } + +- public ServerStatus.Favicon toMojang() { ++ public String toMojang() { + return internal; + } + diff --git a/patches/1.19.2/Common/src/main/resources/craterlib.mixins.json.patch b/patches/1.19.2/Common/src/main/resources/craterlib.mixins.json.patch new file mode 100644 index 0000000..30e41c2 --- /dev/null +++ b/patches/1.19.2/Common/src/main/resources/craterlib.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Common/src/main/resources/craterlib.mixins.json ++++ b/Common/src/main/resources/craterlib.mixins.json +@@ -17,7 +17,8 @@ + "events.PlayerAdvancementsMixin", + "events.PlayerListMixin", + "events.ServerPlayerMixin", +- "events.ServerStatusMixin" ++ "events.ServerStatusMixin", ++ "events.ServerStatusPacketListenerMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.19.2/Fabric/build.gradle.patch b/patches/1.19.2/Fabric/build.gradle.patch index 5af4dbc..6d35248 100644 --- a/patches/1.19.2/Fabric/build.gradle.patch +++ b/patches/1.19.2/Fabric/build.gradle.patch @@ -4,8 +4,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + setDisplayName("[FABRIC/QUILT 1.19.2] CraterLib - ${project.version}") + setGameVersions("1.19.2") setLoaders("fabric", "quilt") diff --git a/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 73b23d3..f078d8c 100644 --- a/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ cancellable = true ) - private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { -- Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(PlayerChatMessage arg, CallbackInfoReturnable ci) { + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), arg.serverContent().getString(), ChatUtils.mojangToAdventure(arg.serverContent())); CraterEventBus.INSTANCE.postEvent(event); diff --git a/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..3026880 --- /dev/null +++ b/patches/1.19.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,55 @@ +--- a/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,52 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.19.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch b/patches/1.19.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch new file mode 100644 index 0000000..8586f71 --- /dev/null +++ b/patches/1.19.2/Fabric/src/main/resources/craterlib.fabric.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Fabric/src/main/resources/craterlib.fabric.mixins.json ++++ b/Fabric/src/main/resources/craterlib.fabric.mixins.json +@@ -9,8 +9,7 @@ + "TutorialMixin" + ], + "server": [ +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" ++ "ServerGamePacketListenerImplMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 new file mode 100644 index 0000000..b5243f2 --- /dev/null +++ b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -0,0 +1,24 @@ +--- 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 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; + import net.minecraft.client.Minecraft; + import net.minecraft.client.gui.screens.Screen; +@@ -28,9 +28,9 @@ + */ + @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)) diff --git a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index e153a68..e62e699 100644 --- a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ cancellable = true ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(PlayerChatMessage arg, CallbackInfoReturnable ci) { + CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), arg.serverContent().getString(), ChatUtils.mojangToAdventure(arg.serverContent())); CraterEventBus.INSTANCE.postEvent(event); diff --git a/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..e22d953 --- /dev/null +++ b/patches/1.19.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.19.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch b/patches/1.19.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch new file mode 100644 index 0000000..1c81cc5 --- /dev/null +++ b/patches/1.19.2/Forge/src/main/resources/craterlib.forge.mixins.json.patch @@ -0,0 +1,12 @@ +--- a/Forge/src/main/resources/craterlib.forge.mixins.json ++++ b/Forge/src/main/resources/craterlib.forge.mixins.json +@@ -9,8 +9,7 @@ + "ConfigScreenHandlerMixin" + ], + "server": [ +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" ++ "ServerGamePacketListenerImplMixin" + ], + "injectors": { + "defaultRequire": 1 diff --git a/patches/1.19.2/NeoForge/build.gradle.patch b/patches/1.19.2/NeoForge/build.gradle.patch index 4a155d8..2ee2c57 100644 --- a/patches/1.19.2/NeoForge/build.gradle.patch +++ b/patches/1.19.2/NeoForge/build.gradle.patch @@ -109,8 +109,8 @@ - setVersionType("release") - setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") 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 a0d386b..6f63513 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 @@ -5,8 +5,8 @@ - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -54,9 +54,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch index 0dab07e..3176ade 100644 --- a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch +++ b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch @@ -1,12 +1,11 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,41 +1,0 @@ -package com.hypherionmc.craterlib.common; - -import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; -import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; -import com.hypherionmc.craterlib.core.event.CraterEventBus; --import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -39,8 +38,7 @@ - - @SubscribeEvent - public void onCommandRegister(RegisterCommandsEvent event) { -- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); -- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); - } - -} diff --git a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 631e6a6..2f716b8 100644 --- a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ - cancellable = true - ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); - CraterEventBus.INSTANCE.postEvent(event); - if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..555de60 --- /dev/null +++ b/patches/1.19.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.19.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.19.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index 3732d52..075f957 100644 --- a/patches/1.19.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.19.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -1,6 +1,6 @@ --- a/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ /dev/null -@@ -1,15 +1,0 @@ +@@ -1,16 +1,0 @@ -{ - "required": true, - "minVersion": "0.8", @@ -10,7 +10,8 @@ - ], - "client": [], - "server": [ -- "ServerGamePacketListenerImplMixin" +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" - ], - "injectors": { - "defaultRequire": 1 diff --git a/patches/1.19.2/README.md.patch b/patches/1.19.2/README.md.patch index 5fd4d92..4aea58e 100644 --- a/patches/1.19.2/README.md.patch +++ b/patches/1.19.2/README.md.patch @@ -1,12 +1,10 @@ --- a/README.md +++ b/README.md -@@ -15,8 +15,7 @@ +@@ -15,7 +15,6 @@ | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | --| 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.19.2/build.gradle.patch b/patches/1.19.2/build.gradle.patch index 4252ab9..b6610ca 100644 --- a/patches/1.19.2/build.gradle.patch +++ b/patches/1.19.2/build.gradle.patch @@ -11,9 +11,9 @@ group = rootProject.group -@@ -58,7 +58,7 @@ - shade "me.hypherionmc.moon-config:core:${moon_config}" +@@ -59,7 +59,7 @@ shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" - shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" + shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" diff --git a/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch index 72841ab..29cfe6e 100644 --- a/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.19.3/.jenkins/Jenkinsfile.snapshot.patch @@ -6,12 +6,12 @@ -def JDK = "21"; -def majorMc = "1.21"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.19.3"; +def modLoaders = "forge|fabric|quilt"; -+def supportedMc = "1.19.3|1.19.4"; ++def supportedMc = "1.19.4"; +def reltype = "snapshot"; pipeline { 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 1a77936..ebe8f79 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 @@ -3,7 +3,7 @@ @@ -2,9 +2,9 @@ import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; - import com.hypherionmc.craterlib.core.config.ModuleConfig; + import com.hypherionmc.craterlib.core.config.AbstractConfig; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; diff --git a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch index 2decb2b..bcb0d0f 100644 --- a/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch +++ b/patches/1.19.3/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java.patch @@ -8,4 +8,4 @@ + internal.sendSuccess(ChatUtils.adventureToMojang(supplier.get()), bl); } - public CommandSourceStack toMojang() { + public void sendFailure(Component text) { diff --git a/patches/1.19.3/Fabric/build.gradle.patch b/patches/1.19.3/Fabric/build.gradle.patch index 33b5cca..2f95c6e 100644 --- a/patches/1.19.3/Fabric/build.gradle.patch +++ b/patches/1.19.3/Fabric/build.gradle.patch @@ -4,10 +4,10 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") -+ setDisplayName("[FABRIC/QUILT 1.19.3/1.19.4] CraterLib - ${project.version}") -+ setGameVersions("1.19.3", "1.19.4") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") ++ setDisplayName("[FABRIC/QUILT 1.19.4] CraterLib - ${project.version}") ++ setGameVersions("1.19.4") setLoaders("fabric", "quilt") setArtifact(remapJar) setCurseEnvironment("both") diff --git a/patches/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 8dbf2df..b3e764e 100644 --- a/patches/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.3/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -21,11 +21,12 @@ cancellable = true ) - private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { -- Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; +- Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(CompletableFuture completableFuture, CompletableFuture completableFuture2, PlayerChatMessage arg, Executor executor, CallbackInfoReturnable cir) { + Component comp2 = completableFuture2.join(); + Component finalArg = comp2 == null ? arg.decoratedContent() : comp2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); ++ CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.19.3/Fabric/src/main/resources/fabric.mod.json.patch b/patches/1.19.3/Fabric/src/main/resources/fabric.mod.json.patch index 4cd15d4..7c75111 100644 --- a/patches/1.19.3/Fabric/src/main/resources/fabric.mod.json.patch +++ b/patches/1.19.3/Fabric/src/main/resources/fabric.mod.json.patch @@ -6,7 +6,7 @@ "fabric-api": "*", - "minecraft": ">=1.21", - "java": ">=21" -+ "minecraft": ">=1.19.3", ++ "minecraft": ">=1.19.4", + "java": ">=17" } } diff --git a/patches/1.19.3/Forge/build.gradle.patch b/patches/1.19.3/Forge/build.gradle.patch index ec5adc7..0cbfa3b 100644 --- a/patches/1.19.3/Forge/build.gradle.patch +++ b/patches/1.19.3/Forge/build.gradle.patch @@ -15,8 +15,8 @@ setProjectVersion("${minecraft_version}-${project.version}") - setDisplayName("[Forge 1.20.6] CraterLib - ${project.version}") - setGameVersions("1.20.6") -+ setDisplayName("[Forge 1.19.3/1.19.4] CraterLib - ${project.version}") -+ setGameVersions("1.19.3", "1.19.4") ++ setDisplayName("[Forge 1.19.4] CraterLib - ${project.version}") ++ setGameVersions("1.19.4") setLoaders("forge") setArtifact(remapJar) setCurseEnvironment("both") diff --git a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 new file mode 100644 index 0000000..5f8832b --- /dev/null +++ b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -0,0 +1,22 @@ +--- 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 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; +@@ -28,9 +29,9 @@ + */ + @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)) diff --git a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 411181d..aac1c84 100644 --- a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -9,7 +9,7 @@ @Mixin(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) public class ServerGamePacketListenerImplMixin { -@@ -22,12 +24,13 @@ +@@ -22,13 +24,14 @@ public ServerPlayer player; @Inject( @@ -19,10 +19,12 @@ cancellable = true ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(CompletableFuture completablefuture1, PlayerChatMessage arg, CompletableFuture completablefuture, Void p_248218_, CallbackInfo ci) { + Component comp2 = completablefuture1.join(); + Component finalArg = comp2 == null ? arg.decoratedContent() : comp2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); ++ CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); CraterEventBus.INSTANCE.postEvent(event); if (event.wasCancelled()) + ci.cancel(); diff --git a/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..5c0e6b2 --- /dev/null +++ b/patches/1.19.3/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,11 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java +@@ -44,7 +44,7 @@ + status.version(), + status.favicon(), + status.enforcesSecureChat(), +- status.isModded() ++ status.forgeData() + ) + )); + } diff --git a/patches/1.19.3/Forge/src/main/resources/META-INF/mods.toml.patch b/patches/1.19.3/Forge/src/main/resources/META-INF/mods.toml.patch index 4130163..1268483 100644 --- a/patches/1.19.3/Forge/src/main/resources/META-INF/mods.toml.patch +++ b/patches/1.19.3/Forge/src/main/resources/META-INF/mods.toml.patch @@ -3,7 +3,7 @@ @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[50,)" -+loaderVersion = "[44,)" ++loaderVersion = "[45,)" license = "MIT" issueTrackerURL = "https://github.com/firstdarkdev/craterLib/issues" @@ -12,7 +12,7 @@ modId = "forge" mandatory = true - versionRange = "[50,)" -+ versionRange = "[44,)" ++ versionRange = "[45,)" ordering = "NONE" side = "BOTH" @@ -20,6 +20,6 @@ modId = "minecraft" mandatory = true - versionRange = "[1.20.6,1.21)" -+ versionRange = "[1.19.3,1.20)" ++ versionRange = "[1.19.4,1.20)" ordering = "NONE" side = "BOTH" diff --git a/patches/1.19.3/NeoForge/build.gradle.patch b/patches/1.19.3/NeoForge/build.gradle.patch index 4a155d8..2ee2c57 100644 --- a/patches/1.19.3/NeoForge/build.gradle.patch +++ b/patches/1.19.3/NeoForge/build.gradle.patch @@ -109,8 +109,8 @@ - setVersionType("release") - setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") 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 a0d386b..6f63513 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 @@ -5,8 +5,8 @@ - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -54,9 +54,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch index 0dab07e..3176ade 100644 --- a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch +++ b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch @@ -1,12 +1,11 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,41 +1,0 @@ -package com.hypherionmc.craterlib.common; - -import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; -import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; -import com.hypherionmc.craterlib.core.event.CraterEventBus; --import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -39,8 +38,7 @@ - - @SubscribeEvent - public void onCommandRegister(RegisterCommandsEvent event) { -- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); -- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); - } - -} diff --git a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 631e6a6..2f716b8 100644 --- a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ - cancellable = true - ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); - CraterEventBus.INSTANCE.postEvent(event); - if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..555de60 --- /dev/null +++ b/patches/1.19.3/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.19.3/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.19.3/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index 3732d52..075f957 100644 --- a/patches/1.19.3/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.19.3/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -1,6 +1,6 @@ --- a/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ /dev/null -@@ -1,15 +1,0 @@ +@@ -1,16 +1,0 @@ -{ - "required": true, - "minVersion": "0.8", @@ -10,7 +10,8 @@ - ], - "client": [], - "server": [ -- "ServerGamePacketListenerImplMixin" +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" - ], - "injectors": { - "defaultRequire": 1 diff --git a/patches/1.19.3/README.md.patch b/patches/1.19.3/README.md.patch index c98b984..f5eeb1f 100644 --- a/patches/1.19.3/README.md.patch +++ b/patches/1.19.3/README.md.patch @@ -5,8 +5,8 @@ | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | - | 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | ++ - ❌ - Not Supported; no bug fixes or new features. - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.19.3/build.gradle.patch b/patches/1.19.3/build.gradle.patch index 4252ab9..b6610ca 100644 --- a/patches/1.19.3/build.gradle.patch +++ b/patches/1.19.3/build.gradle.patch @@ -11,9 +11,9 @@ group = rootProject.group -@@ -58,7 +58,7 @@ - shade "me.hypherionmc.moon-config:core:${moon_config}" +@@ -59,7 +59,7 @@ shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" - shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" + shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" diff --git a/patches/1.19.3/gradle.properties.patch b/patches/1.19.3/gradle.properties.patch index 8961938..c5f3e52 100644 --- a/patches/1.19.3/gradle.properties.patch +++ b/patches/1.19.3/gradle.properties.patch @@ -5,20 +5,20 @@ # Shared -minecraft_version=1.21 -+minecraft_version=1.19.3 ++minecraft_version=1.19.4 project_group=com.hypherionmc.craterlib # Fabric fabric_loader=0.15.11 -fabric_api=0.100.1+1.21 -+fabric_api=0.76.1+1.19.3 ++fabric_api=0.87.2+1.19.4 # Forge -forge_version=50.0.6 - -# NeoForged -neoforge_version=0.0-beta -+forge_version=44.1.0 ++forge_version=45.3.0 # Dependencies moon_config=1.0.9 diff --git a/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20.2/.jenkins/Jenkinsfile.snapshot.patch index 4f29309..a0f8ec2 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"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20.2"; diff --git a/patches/1.20.2/Fabric/build.gradle.patch b/patches/1.20.2/Fabric/build.gradle.patch index a95e752..8951b6c 100644 --- a/patches/1.20.2/Fabric/build.gradle.patch +++ b/patches/1.20.2/Fabric/build.gradle.patch @@ -4,8 +4,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + setDisplayName("[FABRIC/QUILT 1.20.2] CraterLib - ${project.version}") + setGameVersions("1.20.2") setLoaders("fabric", "quilt") diff --git a/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 new file mode 100644 index 0000000..5f8832b --- /dev/null +++ b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -0,0 +1,22 @@ +--- 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 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; +@@ -28,9 +29,9 @@ + */ + @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)) diff --git a/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..5c0e6b2 --- /dev/null +++ b/patches/1.20.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,11 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java +@@ -44,7 +44,7 @@ + status.version(), + status.favicon(), + status.enforcesSecureChat(), +- status.isModded() ++ status.forgeData() + ) + )); + } diff --git a/patches/1.20.2/NeoForge/build.gradle.patch b/patches/1.20.2/NeoForge/build.gradle.patch index 4a155d8..2ee2c57 100644 --- a/patches/1.20.2/NeoForge/build.gradle.patch +++ b/patches/1.20.2/NeoForge/build.gradle.patch @@ -109,8 +109,8 @@ - setVersionType("release") - setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") 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 a0d386b..6f63513 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 @@ -5,8 +5,8 @@ - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -54,9 +54,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch index 0dab07e..3176ade 100644 --- a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch +++ b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch @@ -1,12 +1,11 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,41 +1,0 @@ -package com.hypherionmc.craterlib.common; - -import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; -import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; -import com.hypherionmc.craterlib.core.event.CraterEventBus; --import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -39,8 +38,7 @@ - - @SubscribeEvent - public void onCommandRegister(RegisterCommandsEvent event) { -- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); -- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); - } - -} diff --git a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 631e6a6..2f716b8 100644 --- a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ - cancellable = true - ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); - CraterEventBus.INSTANCE.postEvent(event); - if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..555de60 --- /dev/null +++ b/patches/1.20.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.20.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.20.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index 3732d52..075f957 100644 --- a/patches/1.20.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.20.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -1,6 +1,6 @@ --- a/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ /dev/null -@@ -1,15 +1,0 @@ +@@ -1,16 +1,0 @@ -{ - "required": true, - "minVersion": "0.8", @@ -10,7 +10,8 @@ - ], - "client": [], - "server": [ -- "ServerGamePacketListenerImplMixin" +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" - ], - "injectors": { - "defaultRequire": 1 diff --git a/patches/1.20.2/README.md.patch b/patches/1.20.2/README.md.patch index 5fd4d92..4aea58e 100644 --- a/patches/1.20.2/README.md.patch +++ b/patches/1.20.2/README.md.patch @@ -1,12 +1,10 @@ --- a/README.md +++ b/README.md -@@ -15,8 +15,7 @@ +@@ -15,7 +15,6 @@ | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | --| 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.20.2/build.gradle.patch b/patches/1.20.2/build.gradle.patch index 4252ab9..b6610ca 100644 --- a/patches/1.20.2/build.gradle.patch +++ b/patches/1.20.2/build.gradle.patch @@ -11,9 +11,9 @@ group = rootProject.group -@@ -58,7 +58,7 @@ - shade "me.hypherionmc.moon-config:core:${moon_config}" +@@ -59,7 +59,7 @@ shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" - shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" + shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" diff --git a/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20.4/.jenkins/Jenkinsfile.snapshot.patch index 2256e1d..4480a99 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"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20.4"; diff --git a/patches/1.20.4/Fabric/build.gradle.patch b/patches/1.20.4/Fabric/build.gradle.patch index 896ec96..3d01528 100644 --- a/patches/1.20.4/Fabric/build.gradle.patch +++ b/patches/1.20.4/Fabric/build.gradle.patch @@ -4,8 +4,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + setDisplayName("[FABRIC/QUILT 1.20.4] CraterLib - ${project.version}") + setGameVersions("1.20.4") setLoaders("fabric", "quilt") diff --git a/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 new file mode 100644 index 0000000..b5243f2 --- /dev/null +++ b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -0,0 +1,24 @@ +--- 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 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; + import net.minecraft.client.Minecraft; + import net.minecraft.client.gui.screens.Screen; +@@ -28,9 +28,9 @@ + */ + @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)) diff --git a/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..5c0e6b2 --- /dev/null +++ b/patches/1.20.4/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,11 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java +@@ -44,7 +44,7 @@ + status.version(), + status.favicon(), + status.enforcesSecureChat(), +- status.isModded() ++ status.forgeData() + ) + )); + } diff --git a/patches/1.20.4/NeoForge/build.gradle.patch b/patches/1.20.4/NeoForge/build.gradle.patch index a551cad..ab54863 100644 --- a/patches/1.20.4/NeoForge/build.gradle.patch +++ b/patches/1.20.4/NeoForge/build.gradle.patch @@ -13,8 +13,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + 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 82b6d77..e6c55f8 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 @@ -5,8 +5,8 @@ -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -31,9 +31,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch b/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch index c701073..4ccd39a 100644 --- a/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch +++ b/patches/1.20.4/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -1,9 +1,10 @@ --- /dev/null +++ b/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java -@@ -1,0 +1,43 @@ +@@ -1,0 +1,44 @@ +package com.hypherionmc.craterlib.mixin; + +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.NoConfigScreen; @@ -31,9 +32,9 @@ + */ + @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)) diff --git a/patches/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index f9348e6..0320eef 100644 --- a/patches/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.20.4/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -9,5 +9,5 @@ + "ConfigScreenHandlerMixin" + ], "server": [ - "ServerGamePacketListenerImplMixin" - ], + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" diff --git a/patches/1.20.4/README.md.patch b/patches/1.20.4/README.md.patch index 5fd4d92..4aea58e 100644 --- a/patches/1.20.4/README.md.patch +++ b/patches/1.20.4/README.md.patch @@ -1,12 +1,10 @@ --- a/README.md +++ b/README.md -@@ -15,8 +15,7 @@ +@@ -15,7 +15,6 @@ | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | --| 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.20/.jenkins/Jenkinsfile.snapshot.patch index 003cf75..9ac7e31 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"; -def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; +-def supportedMc = "1.21|1.21.1"; -def reltype = "port"; +def JDK = "17"; +def majorMc = "1.20"; diff --git a/patches/1.20/Fabric/build.gradle.patch b/patches/1.20/Fabric/build.gradle.patch index ce24ce9..8fec041 100644 --- a/patches/1.20/Fabric/build.gradle.patch +++ b/patches/1.20/Fabric/build.gradle.patch @@ -4,8 +4,8 @@ setVersionType("release") setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[FABRIC/QUILT 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[FABRIC/QUILT 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") + 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/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 3c8670d..efab272 100644 --- a/patches/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.20/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -9,7 +9,7 @@ @Mixin(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) public class ServerGamePacketListenerImplMixin { -@@ -22,11 +24,12 @@ +@@ -22,13 +24,14 @@ public ServerPlayer player; @Inject( @@ -19,8 +19,12 @@ cancellable = true ) - private void injectChatEvent(PlayerChatMessage arg, Component arg2, FilteredText arg3, CallbackInfo ci) { +- Component finalcomp = arg2 == null ? arg.decoratedContent() : arg2; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(PlayerChatMessage arg, CompletableFuture completableFuture, CompletableFuture completableFuture2, Void void_, CallbackInfo ci) { + Component arg2 = completableFuture2.join(); - Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); ++ Component finalArg = arg2 == null ? arg.decoratedContent() : arg2; ++ CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); CraterEventBus.INSTANCE.postEvent(event); + if (event.wasCancelled()) + ci.cancel(); diff --git a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch new file mode 100644 index 0000000..c99194b --- /dev/null +++ b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java.patch @@ -0,0 +1,20 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java +@@ -3,7 +3,6 @@ + import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; + import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; + import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; + import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; + import net.minecraftforge.event.RegisterCommandsEvent; + import net.minecraftforge.event.server.ServerStartedEvent; +@@ -36,8 +35,7 @@ + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); +- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); ++ CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + + } 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 new file mode 100644 index 0000000..b5243f2 --- /dev/null +++ b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java.patch @@ -0,0 +1,24 @@ +--- 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 @@ + package com.hypherionmc.craterlib.mixin; + + 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.NoConfigScreen; + import net.minecraft.client.Minecraft; + import net.minecraft.client.gui.screens.Screen; +@@ -28,9 +28,9 @@ + */ + @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)) diff --git a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index d9ab294..7903104 100644 --- a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -9,7 +9,7 @@ @Mixin(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) public class ServerGamePacketListenerImplMixin { -@@ -22,11 +24,12 @@ +@@ -22,13 +24,14 @@ public ServerPlayer player; @Inject( @@ -19,8 +19,12 @@ cancellable = true ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); + private void injectChatEvent(CompletableFuture completablefuture1, PlayerChatMessage arg, CompletableFuture completablefuture, Void p_248218_, CallbackInfo ci) { + Component component = completablefuture1.join(); - Component finalArg = component == null ? arg.decoratedContent() : component; - CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); ++ Component finalArg = component == null ? arg.decoratedContent() : component; ++ CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); CraterEventBus.INSTANCE.postEvent(event); + if (event.wasCancelled()) + ci.cancel(); diff --git a/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..5c0e6b2 --- /dev/null +++ b/patches/1.20/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,11 @@ +--- a/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ b/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java +@@ -44,7 +44,7 @@ + status.version(), + status.favicon(), + status.enforcesSecureChat(), +- status.isModded() ++ status.forgeData() + ) + )); + } diff --git a/patches/1.20/NeoForge/build.gradle.patch b/patches/1.20/NeoForge/build.gradle.patch index 4a155d8..2ee2c57 100644 --- a/patches/1.20/NeoForge/build.gradle.patch +++ b/patches/1.20/NeoForge/build.gradle.patch @@ -109,8 +109,8 @@ - setVersionType("release") - setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") - setProjectVersion("${minecraft_version}-${project.version}") -- setDisplayName("[NeoForge 1.21.0] CraterLib - ${project.version}") -- setGameVersions("1.21") +- setDisplayName("[NeoForge 1.21.x] CraterLib - ${project.version}") +- setGameVersions("1.21", "1.21.1") - setLoaders("neoforge") - setArtifact(remapJar) - setCurseEnvironment("both") 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 a0d386b..6f63513 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 @@ -5,8 +5,8 @@ - -import com.hypherionmc.craterlib.api.events.client.LateInitEvent; -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.NoConfigScreen; -import com.hypherionmc.craterlib.core.event.CraterEventBus; -import com.hypherionmc.craterlib.core.platform.ClientPlatform; @@ -54,9 +54,9 @@ - LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); - CraterEventBus.INSTANCE.postEvent(event); - -- ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { +- ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { - if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { -- ModuleConfig config = (ModuleConfig) conf; +- AbstractConfig config = watcher.getLeft(); - ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); - } - }); diff --git a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch index 0dab07e..3176ade 100644 --- a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch +++ b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java.patch @@ -1,12 +1,11 @@ --- a/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java +++ /dev/null -@@ -1,43 +1,0 @@ +@@ -1,41 +1,0 @@ -package com.hypherionmc.craterlib.common; - -import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; -import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; -import com.hypherionmc.craterlib.core.event.CraterEventBus; --import com.hypherionmc.craterlib.nojang.commands.CommandsRegistry; -import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; @@ -39,8 +38,7 @@ - - @SubscribeEvent - public void onCommandRegister(RegisterCommandsEvent event) { -- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); -- CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); +- CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); - } - -} diff --git a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch index 631e6a6..2f716b8 100644 --- a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch +++ b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java.patch @@ -30,8 +30,8 @@ - cancellable = true - ) - private void injectChatEvent(Component component, PlayerChatMessage arg, FilteredText p_296589_, CallbackInfo ci) { -- Component finalArg = component == null ? arg.decoratedContent() : component; -- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalArg.getString(), ChatUtils.mojangToAdventure(finalArg)); +- Component finalcomp = component == null ? arg.decoratedContent() : component; +- CraterServerChatEvent event = new CraterServerChatEvent(BridgedPlayer.of(this.player), finalcomp.getString(), ChatUtils.mojangToAdventure(finalcomp)); - CraterEventBus.INSTANCE.postEvent(event); - if (event.wasCancelled()) - ci.cancel(); diff --git a/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch new file mode 100644 index 0000000..555de60 --- /dev/null +++ b/patches/1.20/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java.patch @@ -0,0 +1,56 @@ +--- a/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java ++++ /dev/null +@@ -1,53 +1,0 @@ +-package com.hypherionmc.craterlib.mixin; +- +-import com.hypherionmc.craterlib.api.events.server.ServerStatusEvent; +-import com.hypherionmc.craterlib.core.event.CraterEventBus; +-import com.hypherionmc.craterlib.utils.ChatUtils; +-import net.minecraft.network.Connection; +-import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +-import net.minecraft.network.protocol.status.ServerStatus; +-import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; +-import net.minecraft.server.network.ServerStatusPacketListenerImpl; +-import org.spongepowered.asm.mixin.Final; +-import org.spongepowered.asm.mixin.Mixin; +-import org.spongepowered.asm.mixin.Shadow; +-import org.spongepowered.asm.mixin.injection.At; +-import org.spongepowered.asm.mixin.injection.Inject; +-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +- +-@Mixin(ServerStatusPacketListenerImpl.class) +-public class ServerStatusPacketListenerMixin { +- +- @Shadow +- @Final +- private ServerStatus status; +- +- @Shadow @Final private Connection connection; +- +- @Inject(method = "handleStatusRequest", +- at = @At( +- value = "INVOKE", +- target = "Lnet/minecraft/network/Connection;send(Lnet/minecraft/network/protocol/Packet;)V", +- shift = At.Shift.BEFORE), +- cancellable = true +- ) +- private void injectHandleStatusRequest(ServerboundStatusRequestPacket arg, CallbackInfo ci) { +- ServerStatusEvent.StatusRequestEvent event = new ServerStatusEvent.StatusRequestEvent(ChatUtils.mojangToAdventure(status.description())); +- CraterEventBus.INSTANCE.postEvent(event); +- +- if (event.getNewStatus() != null) { +- ci.cancel(); +- this.connection.send(new ClientboundStatusResponsePacket( +- new ServerStatus(ChatUtils.adventureToMojang( +- event.getNewStatus()), +- status.players(), +- status.version(), +- status.favicon(), +- status.enforcesSecureChat(), +- status.isModded() +- ) +- )); +- } +- } +- +-} diff --git a/patches/1.20/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch b/patches/1.20/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch index 3732d52..075f957 100644 --- a/patches/1.20/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch +++ b/patches/1.20/NeoForge/src/main/resources/craterlib.neoforge.mixins.json.patch @@ -1,6 +1,6 @@ --- a/NeoForge/src/main/resources/craterlib.neoforge.mixins.json +++ /dev/null -@@ -1,15 +1,0 @@ +@@ -1,16 +1,0 @@ -{ - "required": true, - "minVersion": "0.8", @@ -10,7 +10,8 @@ - ], - "client": [], - "server": [ -- "ServerGamePacketListenerImplMixin" +- "ServerGamePacketListenerImplMixin", +- "ServerStatusPacketListenerMixin" - ], - "injectors": { - "defaultRequire": 1 diff --git a/patches/1.20/README.md.patch b/patches/1.20/README.md.patch index 5fd4d92..4aea58e 100644 --- a/patches/1.20/README.md.patch +++ b/patches/1.20/README.md.patch @@ -1,12 +1,10 @@ --- a/README.md +++ b/README.md -@@ -15,8 +15,7 @@ +@@ -15,7 +15,6 @@ | < 1.18.2 | ❌ | | 1.18.2-1.20.2 | ✳️ | | 1.20.4 | ✳️ | -| 1.20.6 | ❌ | --| 1.21 | ✳️ | -+| 1.21 | 🚧 | + | 1.21.x | ✳️ | - ❌ - Not Supported; no bug fixes or new features. - - 🚧 - Work in Progress; not ready for release. diff --git a/patches/1.20/build.gradle.patch b/patches/1.20/build.gradle.patch index e17823d..02a0314 100644 --- a/patches/1.20/build.gradle.patch +++ b/patches/1.20/build.gradle.patch @@ -19,9 +19,9 @@ group = rootProject.group -@@ -58,7 +57,7 @@ - shade "me.hypherionmc.moon-config:core:${moon_config}" +@@ -59,7 +58,7 @@ shade "me.hypherionmc.moon-config:toml:${moon_config}" + shade "me.hypherionmc.moon-config:json:${moon_config}" shade "com.hypherionmc:rpcsdk:${rpc_sdk}" - shade "me.hypherionmc.sdlink:mcdiscordformatter-1.20.3:${discord_formatter}" + shade "me.hypherionmc.sdlink:mcdiscordformatter-1.19.1:${discord_formatter}" diff --git a/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch index 0c85330..c82758c 100644 --- a/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch +++ b/patches/1.21/.jenkins/Jenkinsfile.snapshot.patch @@ -1,12 +1,10 @@ --- a/.jenkins/Jenkinsfile.snapshot +++ b/.jenkins/Jenkinsfile.snapshot -@@ -3,8 +3,8 @@ - def JDK = "21"; +@@ -4,7 +4,7 @@ def majorMc = "1.21"; def modLoaders = "neoforge|fabric|quilt"; --def supportedMc = "1.21"; + def supportedMc = "1.21|1.21.1"; -def reltype = "port"; -+def supportedMc = "1.21|1.21.1"; +def reltype = "snapshot"; pipeline { diff --git a/patches/1.21/README.md.patch b/patches/1.21/README.md.patch new file mode 100644 index 0000000..4aea58e --- /dev/null +++ b/patches/1.21/README.md.patch @@ -0,0 +1,10 @@ +--- a/README.md ++++ b/README.md +@@ -15,7 +15,6 @@ + | < 1.18.2 | ❌ | + | 1.18.2-1.20.2 | ✳️ | + | 1.20.4 | ✳️ | +-| 1.20.6 | ❌ | + | 1.21.x | ✳️ | + + - ❌ - Not Supported; no bug fixes or new features.