diff --git a/1.21.2/.gitattributes b/1.21.2/.gitattributes new file mode 100644 index 0000000..20fc528 --- /dev/null +++ b/1.21.2/.gitattributes @@ -0,0 +1,15 @@ +* text eol=lf +*.bat text eol=crlf +*.patch text eol=lf +*.java text eol=lf +*.gradle text eol=crlf +*.png binary +*.gif binary +*.exe binary +*.dll binary +*.jar binary +*.lzma binary +*.zip binary +*.pyd binary +*.cfg text eol=lf +*.jks binary \ No newline at end of file diff --git a/1.21.2/.gitignore b/1.21.2/.gitignore new file mode 100644 index 0000000..966dad4 --- /dev/null +++ b/1.21.2/.gitignore @@ -0,0 +1,29 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run + +artifacts +src/test/** + +workspace +upstream +rejects \ No newline at end of file diff --git a/1.21.2/.jenkins/Jenkinsfile.deploy b/1.21.2/.jenkins/Jenkinsfile.deploy new file mode 100644 index 0000000..56e71cc --- /dev/null +++ b/1.21.2/.jenkins/Jenkinsfile.deploy @@ -0,0 +1,56 @@ +def JDK = "21" +def majorMc = "1.21.2"; + +pipeline { + agent { + docker { + image "registry.firstdark.dev/java${JDK}:latest" + alwaysPull true + args '-v gradle-cache:/home/gradle/.gradle' + } + } + + environment { + GRADLE_USER_HOME = '/home/gradle/.gradle' + } + + stages { + stage("Notify Discord") { + steps { + discordSend webhookURL: env.FDD_WH_ADMIN, + title: "Deploy Started: CraterLib ${majorMc} Deploy #${BUILD_NUMBER}", + link: env.BUILD_URL, + result: 'SUCCESS', + description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})" + } + } + stage("Prepare") { + steps { + sh "chmod +x ./gradlew" + sh "./gradlew clean" + } + } + stage("Publish to Modrinth/Curseforge") { + steps { + sh "./gradlew publishMod -Prelease=true" + } + } + stage("Publish to Maven") { + steps { + sh "./gradlew publish -Prelease=true" + } + } + } + post { + always { + sh "./gradlew --stop" + deleteDir() + + discordSend webhookURL: env.FDD_WH_ADMIN, + title: "CraterLib Port Deploy #${BUILD_NUMBER}", + link: env.BUILD_URL, + result: currentBuild.currentResult, + description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})\nStatus: ${currentBuild.currentResult}" + } + } +} diff --git a/1.21.2/.jenkins/Jenkinsfile.snapshot b/1.21.2/.jenkins/Jenkinsfile.snapshot new file mode 100644 index 0000000..302e42f --- /dev/null +++ b/1.21.2/.jenkins/Jenkinsfile.snapshot @@ -0,0 +1,68 @@ +def projectName = "CraterLib"; +def projectIcon = "https://cdn.modrinth.com/data/Nn8Wasaq/a172c634683a11a2e9ae593e56eba7885743bb44.png"; +def JDK = "21"; +def majorMc = "1.21.2"; +def modLoaders = "neoforge|fabric|quilt"; +def supportedMc = "1.21.2"; +def reltype = "release"; + +pipeline { + agent { + docker { + image "registry.firstdark.dev/java${JDK}:latest" + alwaysPull true + args '-v gradle-cache:/home/gradle/.gradle' + } + } + + environment { + GRADLE_USER_HOME = '/home/gradle/.gradle' + } + + stages { + stage("Notify Discord") { + steps { + discordSend webhookURL: env.SSS_WEBHOOK, + title: "Deploy Started: ${projectName} ${majorMc} Deploy #${BUILD_NUMBER}", + link: env.BUILD_URL, + result: 'SUCCESS', + description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})" + } + } + + stage("Prepare") { + steps { + sh "chmod +x ./gradlew" + sh "./gradlew build -PreleaseType=${reltype}" + } + } + + stage("Publish to Maven") { + steps { + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + sh "./gradlew publish -PreleaseType=${reltype}" + } + } + } + } + + post { + always { + sh "./gradlew --stop" + archiveArtifacts artifacts: 'artifacts/*.jar' + + fddsnapshotter apiKey: env.PLATFORM_KEY, + projectSlug: "craterlib", + projectName: "${projectName}", + projectIcon: "${projectIcon}", + modLoaders: "${modLoaders}", + minecraftVersions: "${supportedMc}", + type: "snapshot", + dependsOn: "", + failWebhook: env.SSS_WEBHOOK, + publishWebhooks: "${env.SSS_WEBHOOK}|${env.FDD_WH}" + + deleteDir() + } + } +} \ No newline at end of file diff --git a/1.21.2/Common/build.gradle b/1.21.2/Common/build.gradle new file mode 100644 index 0000000..15a4dec --- /dev/null +++ b/1.21.2/Common/build.gradle @@ -0,0 +1,68 @@ +archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}" + +dependencies { + stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") +} + +shadowJar { + from sourceSets.main.output + configurations = [project.configurations.shade] + + dependencies { + exclude(dependency('com.google.code.gson:.*')) + + relocate 'me.hypherionmc.moonconfig', 'shadow.hypherionmc.moonconfig' + relocate 'me.hypherionmc.mcdiscordformatter', 'shadow.hypherionmc.mcdiscordformatter' + relocate 'net.kyori', 'shadow.kyori' + } + setArchiveClassifier("dev") +} + +/** + * =============================================================================== + * = DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING = + * =============================================================================== + */ + +unimined.minecraft { + fabric { + loader fabric_loader + } + + defaultRemapJar = false +} + +processResources { + def buildProps = project.properties.clone() + + filesMatching(['pack.mcmeta']) { + expand buildProps + } +} + +/** + * Publishing Config + */ +publishing { + publications { + mavenCommon(MavenPublication) { + artifactId project.archivesBaseName + from components.java + + pom.withXml { + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + it.artifactId.text() == 'regutils-joined-fabric' || + it.artifactId.text() == 'core' || + it.artifactId.text() == 'toml' + }.each() { + it.parent().remove(it) + } + } + } + } + + repositories { + maven rootProject.orion.getPublishingMaven() + } +} \ No newline at end of file diff --git a/1.21.2/Common/gradle.properties b/1.21.2/Common/gradle.properties new file mode 100644 index 0000000..65fcf04 --- /dev/null +++ b/1.21.2/Common/gradle.properties @@ -0,0 +1,2 @@ +# We don't need the common jar to be remapped +fabric.loom.dontRemap=true \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/CraterConstants.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/CraterConstants.java new file mode 100644 index 0000000..93edffc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/CraterConstants.java @@ -0,0 +1,10 @@ +package com.hypherionmc.craterlib; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CraterConstants { + public static final String MOD_ID = "craterlib"; + public static final String MOD_NAME = "CraterLib"; + public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java new file mode 100644 index 0000000..35f29f0 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/commands/CraterCommand.java @@ -0,0 +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.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 net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; +import java.util.function.Consumer; + +public class CraterCommand { + + private final LiteralArgumentBuilder mojangCommand; + private int permLevel = 4; + private String luckPermNode = ""; + + CraterCommand(LiteralArgumentBuilder cmd) { + this.mojangCommand = cmd; + } + + public static CraterCommand literal(String commandName) { + return new CraterCommand(Commands.literal(commandName)); + } + + public CraterCommand requiresPermission(int perm) { + this.permLevel = perm; + this.mojangCommand.requires(this::checkPermission); + return this; + } + + 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) { + return this.execute(stack -> { + ctx.accept(stack); + return 1; + }); + } + + @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.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterClientTickEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterClientTickEvent.java new file mode 100644 index 0000000..c68c596 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterClientTickEvent.java @@ -0,0 +1,14 @@ +package com.hypherionmc.craterlib.api.events.client; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class CraterClientTickEvent extends CraterEvent { + + private final BridgedClientLevel level; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterSinglePlayerEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterSinglePlayerEvent.java new file mode 100644 index 0000000..ab671f0 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/CraterSinglePlayerEvent.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.api.events.client; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class CraterSinglePlayerEvent extends CraterEvent { + + private final BridgedPlayer player; + + public static class PlayerLogin extends CraterSinglePlayerEvent { + + public PlayerLogin(BridgedPlayer player) { + super(player); + } + + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/LateInitEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/LateInitEvent.java new file mode 100644 index 0000000..33c06fb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/LateInitEvent.java @@ -0,0 +1,16 @@ +package com.hypherionmc.craterlib.api.events.client; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.BridgedOptions; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class LateInitEvent extends CraterEvent { + + private final BridgedMinecraft minecraft; + private final BridgedOptions options; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/PlayerJoinRealmEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/PlayerJoinRealmEvent.java new file mode 100644 index 0000000..1ef95de --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/PlayerJoinRealmEvent.java @@ -0,0 +1,14 @@ +package com.hypherionmc.craterlib.api.events.client; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.realmsclient.dto.BridgedRealmsServer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class PlayerJoinRealmEvent extends CraterEvent { + + private final BridgedRealmsServer server; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/ScreenEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/ScreenEvent.java new file mode 100644 index 0000000..034ca4a --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/client/ScreenEvent.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.api.events.client; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.client.gui.BridgedScreen; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@RequiredArgsConstructor +public class ScreenEvent extends CraterEvent { + + private final BridgedScreen screen; + + @Getter + public static class Opening extends ScreenEvent { + + private final BridgedScreen currentScreen; + @Setter private BridgedScreen newScreen; + + public Opening(BridgedScreen currentScreen, BridgedScreen newScreen) { + super(newScreen); + this.currentScreen = currentScreen; + this.newScreen = newScreen; + } + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/common/CraterPlayerDeathEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/common/CraterPlayerDeathEvent.java new file mode 100644 index 0000000..02cd63c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/common/CraterPlayerDeathEvent.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.api.events.common; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.world.damagesource.DamageSource; + +@RequiredArgsConstructor +@Getter +public class CraterPlayerDeathEvent extends CraterEvent { + + private final BridgedPlayer player; + private final DamageSource damageSource; + + public Component getDeathMessage() { + return ChatUtils.mojangToAdventure(damageSource.getLocalizedDeathMessage(player.toMojang())); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterAdvancementEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterAdvancementEvent.java new file mode 100644 index 0000000..5cd659d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterAdvancementEvent.java @@ -0,0 +1,34 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.advancements.BridgedAdvancement; +import com.hypherionmc.craterlib.nojang.advancements.BridgedDisplayInfo; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import net.kyori.adventure.text.Component; + +import java.util.Optional; + +@Getter +public class CraterAdvancementEvent extends CraterEvent { + + private final BridgedAdvancement advancement; + private final BridgedPlayer player; + private final Component title; + private final Component description; + + public CraterAdvancementEvent(BridgedPlayer player, BridgedAdvancement advancement) { + this.advancement = advancement; + this.player = player; + + Optional displayInfo = advancement.displayInfo(); + + if (displayInfo.isPresent()) { + this.title = displayInfo.get().displayName(); + this.description = displayInfo.get().description(); + } else { + this.title = Component.text("Unknown"); + this.description = Component.text("Unknown"); + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterCommandEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterCommandEvent.java new file mode 100644 index 0000000..364cc22 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterCommandEvent.java @@ -0,0 +1,58 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.StringRange; +import lombok.Getter; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.ComponentArgument; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.Nullable; + +@Getter +public class CraterCommandEvent extends CraterEvent { + + private final ParseResults parseResults; + @Setter private Throwable exception; + private final String command; + + private CraterCommandEvent(ParseResults parseResults, String command) { + this.parseResults = parseResults; + this.command = command; + } + + public static CraterCommandEvent of(ParseResults stack, String command) { + return new CraterCommandEvent(stack, command); + } + + public String getCommandString() { + return parseResults.getReader().getString(); + } + + @Nullable + public BridgedPlayer getPlayer() { + try { + Player p = parseResults.getContext().getLastChild().getSource().getPlayer(); + + if (p != null) + return BridgedPlayer.of(p); + } catch (Exception ignored) {} + + return null; + } + + public String getTarget() { + CommandContext context = parseResults.getContext().build(parseResults.getReader().getString()); + StringRange selector_range = parseResults.getContext().getArguments().get("targets").getRange(); + return context.getInput().substring(selector_range.getStart(), selector_range.getEnd()); + } + + public Component getMessage() { + return ChatUtils.mojangToAdventure(ComponentArgument.getComponent(parseResults.getContext().build(parseResults.getReader().getString()), "message")); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java new file mode 100644 index 0000000..9e7d7a2 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterPlayerEvent.java @@ -0,0 +1,44 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class CraterPlayerEvent extends CraterEvent { + + private final BridgedPlayer player; + + @Getter + public static class PlayerLoggedIn extends CraterPlayerEvent { + private final boolean isFromVanish; + + public PlayerLoggedIn(BridgedPlayer player) { + this(player, false); + } + + public PlayerLoggedIn(BridgedPlayer player, boolean isFromVanish) { + super(player); + this.isFromVanish = isFromVanish; + } + + } + + @Getter + public static class PlayerLoggedOut extends CraterPlayerEvent { + private final boolean isFromVanish; + + public PlayerLoggedOut(BridgedPlayer player) { + this(player, false); + } + + public PlayerLoggedOut(BridgedPlayer player, boolean isFromVanish) { + super(player); + this.isFromVanish = isFromVanish; + } + + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java new file mode 100644 index 0000000..5adac03 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterRegisterCommandEvent.java @@ -0,0 +1,18 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.api.commands.CraterCommand; +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.mojang.brigadier.CommandDispatcher; +import lombok.AllArgsConstructor; +import net.minecraft.commands.CommandSourceStack; + +@AllArgsConstructor +public class CraterRegisterCommandEvent extends CraterEvent { + + private final CommandDispatcher stack; + + public void registerCommand(CraterCommand cmd) { + cmd.register(stack); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerChatEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerChatEvent.java new file mode 100644 index 0000000..b25170a --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerChatEvent.java @@ -0,0 +1,25 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.core.event.annot.Cancellable; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import lombok.Setter; +import net.kyori.adventure.text.Component; + +@Cancellable +@Getter +public class CraterServerChatEvent extends CraterEvent { + + public final String message, username; + public final BridgedPlayer player; + @Setter private Component component; + + public CraterServerChatEvent(BridgedPlayer player, String message, Component component) { + this.message = message; + this.player = player; + this.username = player.getGameProfile().getName(); + this.component = component; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerLifecycleEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerLifecycleEvent.java new file mode 100644 index 0000000..3f87d04 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/CraterServerLifecycleEvent.java @@ -0,0 +1,34 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +public class CraterServerLifecycleEvent extends CraterEvent { + + @RequiredArgsConstructor + @Getter + public static class Starting extends CraterServerLifecycleEvent { + private final BridgedMinecraftServer server; + } + + @RequiredArgsConstructor + @Getter + public static class Started extends CraterServerLifecycleEvent { + private final BridgedMinecraftServer server; + } + + @RequiredArgsConstructor + @Getter + public static class Stopping extends CraterServerLifecycleEvent { + private final BridgedMinecraftServer server; + } + + @RequiredArgsConstructor + @Getter + public static class Stopped extends CraterServerLifecycleEvent { + private final BridgedMinecraftServer server; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/MessageBroadcastEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/MessageBroadcastEvent.java new file mode 100644 index 0000000..6b11404 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/MessageBroadcastEvent.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; + +import java.util.function.Function; + +@RequiredArgsConstructor +@Getter +public class MessageBroadcastEvent extends CraterEvent { + + private final Component component; + private final Function function; + private final boolean bl; + private final String threadName; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/PlayerPreLoginEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/PlayerPreLoginEvent.java new file mode 100644 index 0000000..eaabffd --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/PlayerPreLoginEvent.java @@ -0,0 +1,20 @@ +package com.hypherionmc.craterlib.api.events.server; + +import com.hypherionmc.craterlib.core.event.CraterEvent; +import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.kyori.adventure.text.Component; + +import java.net.SocketAddress; + +@RequiredArgsConstructor +@Getter +public class PlayerPreLoginEvent extends CraterEvent { + + private final SocketAddress address; + private final BridgedGameProfile gameProfile; + @Setter private Component message; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/events/server/ServerStatusEvent.java new file mode 100644 index 0000000..b58dafa --- /dev/null +++ b/1.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CommonPacketWrapper.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CommonPacketWrapper.java new file mode 100644 index 0000000..fe94544 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CommonPacketWrapper.java @@ -0,0 +1,19 @@ +package com.hypherionmc.craterlib.api.networking; + +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +public record CommonPacketWrapper(PacketHolder container, T packet) implements CustomPacketPayload +{ + public void encode(BridgedFriendlyByteBuf buf) + { + container().encoder().accept(packet(), buf); + } + + @Override + public Type type() + { + return container.type(); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CraterNetworkHandler.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CraterNetworkHandler.java new file mode 100644 index 0000000..6b57e96 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/api/networking/CraterNetworkHandler.java @@ -0,0 +1,28 @@ +package com.hypherionmc.craterlib.api.networking; + +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; + +import java.util.List; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public interface CraterNetworkHandler { + + void sendToServer(T packet); + + void sendToServer(T packet, boolean ignoreCheck); + + void sendToClient(T packet, BridgedPlayer player); + + default void sendToClients(T packet, List players) { + for (BridgedPlayer player : players) { + sendToClient(packet, player); + } + } + + default void sendToAllClients(T packet, BridgedMinecraftServer server) { + sendToClients(packet, server.getPlayers()); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java new file mode 100644 index 0000000..01a7f94 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/CraterConfigScreen.java @@ -0,0 +1,390 @@ +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.AbstractConfig; +import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen; +import com.hypherionmc.craterlib.core.config.annotations.SubConfig; +import com.hypherionmc.craterlib.core.config.annotations.Tooltip; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import me.hypherionmc.moonconfig.core.conversion.SpecComment; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.ConfirmScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * @author HypherionSA + */ +public class CraterConfigScreen extends Screen { + public static final float SCROLLBAR_BOTTOM_COLOR = .5f; + public static final float SCROLLBAR_TOP_COLOR = .67f; + private static final int TOP = 26; + private static final int BOTTOM = 24; + private final Screen parent; + private final List> options = new ArrayList<>(); + private final AbstractConfig config; + public double scrollerAmount; + private boolean dragging; + + public CraterConfigScreen(AbstractConfig config, Screen parent, Object subConfig) { + super(Component.translatable("cl." + config.getClass().getSimpleName().toLowerCase() + ".title")); + this.parent = parent; + this.config = config; + if (subConfig != null) { + setupScreenFromConfig(subConfig, subConfig.getClass()); + } else { + setupScreenFromConfig(config, config.getClass()); + } + } + + public CraterConfigScreen(AbstractConfig config, Screen parent) { + this(config, parent, null); + } + + private static Component toText(Enum val) { + return Component.translatable(val.toString()); + } + + private static Component toText(Boolean bool) { + return Component.translatable(bool.toString()); + } + + private void setupScreenFromConfig(Object object, Class clazz) { + while (clazz != Object.class) { + for (Field field : clazz.getDeclaredFields()) { + final int fieldModifiers = field.getModifiers(); + if (object == null || Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers)) { + continue; + } + + try { + if (!field.isAccessible()) { + field.setAccessible(true); + } + if (field.isAnnotationPresent(HideFromScreen.class)) + return; + Object val = field.get(object); + + /* Lang Stuff */ + String baseLangKey = "cl." + clazz.getSimpleName().toLowerCase() + "." + field.getName().toLowerCase(); + String[] tooltipLang = field.isAnnotationPresent(SpecComment.class) ? new String[]{field.getAnnotation(SpecComment.class).value()} : new String[0]; + if (field.isAnnotationPresent(Tooltip.class)) { + tooltipLang = field.getAnnotation(Tooltip.class).value(); + } + + add(Component.translatable(baseLangKey), + val, + () -> val, + (ret) -> { + try { + field.set(object, ret); + config.saveConfig(config); + } catch (IllegalAccessException e) { + CraterConstants.LOG.error("Failed to update value for field {} in config {}", field.getName(), config.getConfigName(), e); + } + }, + field.isAnnotationPresent(SubConfig.class), + tooltipLang + ); + } catch (IllegalAccessException e) { + CraterConstants.LOG.error("Failed to access value for field {} in config {}", field.getName(), config.getConfigName(), e); + } + } + clazz = clazz.getSuperclass(); + } + } + + public void add(Component text, T value, @Nullable Supplier defaultValue, Consumer savingConsumer, boolean isSubConfig, String... langKeys) { + Option option = (Option) createOption(value, isSubConfig); + option.text = text; + option.defaultValue = defaultValue; + option.savingConsumer = savingConsumer; + option.originalValue = value; + option.value = value; + option.setLangKeys(List.of(langKeys)); + options.add(option); + option.onAdd(); + } + + private Option createOption(T value, boolean isSubConfig) { + if (value instanceof Enum) { + Object[] objects = value.getClass().getEnumConstants(); + return new ToggleButton>((List) Arrays.asList(objects), CraterConfigScreen::toText); + } + if (value instanceof Boolean) { + return new ToggleButton<>(Arrays.asList(Boolean.TRUE, Boolean.FALSE), CraterConfigScreen::toText); + } + if (value instanceof String) { + return new TextConfigOption<>(Function.identity(), Function.identity()); + } + if (value instanceof Integer) { + return new TextConfigOption<>(Objects::toString, Integer::valueOf); + } + if (value instanceof Long) { + return new TextConfigOption<>(Objects::toString, Long::valueOf); + } + if (value instanceof Double) { + return new TextConfigOption<>(Objects::toString, Double::valueOf); + } + if (value instanceof Float) { + return new TextConfigOption<>(Objects::toString, Float::valueOf); + } + if (value instanceof BigInteger) { + return new TextConfigOption<>(Objects::toString, BigInteger::new); + } + if (value instanceof BigDecimal) { + return new TextConfigOption<>(Objects::toString, BigDecimal::new); + } + if (value instanceof ResourceLocation) { + return new TextConfigOption<>(Objects::toString, ResourceLocation::parse); + } + if (isSubConfig) { + return new SubConfigWidget<>(config, this, value); + } + throw new IllegalArgumentException(String.valueOf(value)); + } + + @Override + protected void init() { + super.init(); + ((List) children()).addAll(options); + + int buttonWidths = Math.min(200, (width - 50 - 12) / 3); + addRenderableWidget(new InternalConfigButton(this, width / 2 - buttonWidths - 3, height - 22, buttonWidths, 20, Component.empty(), true)); + addRenderableWidget(new InternalConfigButton(this, width / 2 + 3, height - 22, buttonWidths, 20, Component.empty(), false)); + } + + @Override + public void render(@NotNull GuiGraphics matrices, int mouseX, int mouseY, float delta) { + overlayBackground(matrices.pose(), TOP, height - BOTTOM, 32); + renderScrollBar(); + + matrices.pose().pushPose(); + matrices.pose().translate(0, 0, 500.0); + overlayBackground(matrices.pose(), 0, TOP, 64); + overlayBackground(matrices.pose(), height - BOTTOM, height, 64); + renderShadow(matrices.pose()); + matrices.drawCenteredString(font, getTitle(), width / 2, 9, 0xFFFFFF); + matrices.pose().popPose(); + + super.render(matrices, mouseX, mouseY, delta); + + int y = (int) (TOP + 4 - Math.round(scrollerAmount)); + for (Option option : options) { + int height1 = option.height(); + option.render(minecraft, font, 40, y, width - 80, height1, matrices, mouseX, mouseY, delta); + renderConfigTooltip(matrices, font, mouseX, mouseY, 40, y, font.width(option.text), height1, option.text.getString(), option.getLangKeys().toArray(new String[0])); + y += height1; + } + } + + private void renderScrollBar() { + int listHeight = height - BOTTOM - TOP; + int totalHeight = totalHeight(); + if (totalHeight > listHeight) { + int maxScroll = Math.max(0, totalHeight - listHeight); + int height = listHeight * listHeight / totalHeight; + height = Mth.clamp(height, 32, listHeight); + height = Math.max(10, height); + int minY = Math.min(Math.max((int) scrollerAmount * (listHeight - height) / maxScroll + TOP, TOP), this.height - BOTTOM - height); + + int scrollbarPositionMaxX = width; + int scrollbarPositionMinX = scrollbarPositionMaxX - 6; + + int maxY = this.height - BOTTOM; + //RenderSystem.disableTexture(); + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + //RenderSystem.setShader(RenderSystem.getShader()); + + buffer.addVertex(scrollbarPositionMinX, maxY, 0.0f).setColor(0, 0, 0, 255); + buffer.addVertex(scrollbarPositionMaxX, maxY, 0.0f).setColor(0, 0, 0, 255); + buffer.addVertex(scrollbarPositionMaxX, TOP, 0.0f).setColor(0, 0, 0, 255); + buffer.addVertex(scrollbarPositionMinX, TOP, 0.0f).setColor(0, 0, 0, 255); + + buffer.addVertex(scrollbarPositionMinX, minY + height, 0.0f).setColor(SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, 1); + buffer.addVertex(scrollbarPositionMaxX, minY + height, 0.0f).setColor(SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, 1); + buffer.addVertex(scrollbarPositionMaxX, minY, 0.0f).setColor(SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, 1); + buffer.addVertex(scrollbarPositionMinX, minY, 0.0f).setColor(SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, SCROLLBAR_BOTTOM_COLOR, 1); + buffer.addVertex(scrollbarPositionMinX, (minY + height - 1), 0.0f).setColor(SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, 1); + buffer.addVertex((scrollbarPositionMaxX - 1), (minY + height - 1), 0.0f).setColor(SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, 1); + buffer.addVertex((scrollbarPositionMaxX - 1), minY, 0.0f).setColor(SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, 1); + buffer.addVertex(scrollbarPositionMinX, minY, 0.0f).setColor(SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, SCROLLBAR_TOP_COLOR, 1); + BufferUploader.drawWithShader(buffer.buildOrThrow()); + RenderSystem.disableBlend(); + //RenderSystem.enableTexture(); + } + } + + private void renderShadow(PoseStack matrices) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(770, 771, 0, 1); + //RenderSystem.disableTexture(); + //RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + Matrix4f matrix = matrices.last().pose(); + buffer.addVertex(matrix, 0, TOP + 4, 0.0F).setUv(0, 1).setColor(0, 0, 0, 0); + buffer.addVertex(matrix, width, TOP + 4, 0.0F).setUv(1, 1).setColor(0, 0, 0, 0); + buffer.addVertex(matrix, width, TOP, 0.0F).setUv(1, 0).setColor(0, 0, 0, 185); + buffer.addVertex(matrix, 0, TOP, 0.0F).setUv(0, 0).setColor(0, 0, 0, 185); + buffer.addVertex(matrix, 0, height - BOTTOM, 0.0F).setUv(0, 1).setColor(0, 0, 0, 185); + buffer.addVertex(matrix, width, height - BOTTOM, 0.0F).setUv(1, 1).setColor(0, 0, 0, 185); + buffer.addVertex(matrix, width, height - BOTTOM - 4, 0.0F).setUv(1, 0).setColor(0, 0, 0, 0); + buffer.addVertex(matrix, 0, height - BOTTOM - 4, 0.0F).setUv(0, 0).setColor(0, 0, 0, 0); + BufferUploader.drawWithShader(buffer.buildOrThrow()); + //RenderSystem.enableTexture(); + RenderSystem.disableBlend(); + } + + protected void overlayBackground(PoseStack matrices, int h1, int h2, int color) { + overlayBackground(matrices.last().pose(), 0, h1, width, h2, color, color, color, 255, 255); + } + + protected void overlayBackground(Matrix4f matrix, int minX, int minY, int maxX, int maxY, int red, int green, int blue, int startAlpha, int endAlpha) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + //RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + RenderSystem.setShaderTexture(0, Screen.MENU_BACKGROUND); + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); + buffer.addVertex(matrix, minX, maxY, 0.0F).setUv(minX / 32.0F, maxY / 32.0F).setColor(red, green, blue, endAlpha); + buffer.addVertex(matrix, maxX, maxY, 0.0F).setUv(maxX / 32.0F, maxY / 32.0F).setColor(red, green, blue, endAlpha); + buffer.addVertex(matrix, maxX, minY, 0.0F).setUv(maxX / 32.0F, minY / 32.0F).setColor(red, green, blue, startAlpha); + buffer.addVertex(matrix, minX, minY, 0.0F).setUv(minX / 32.0F, minY / 32.0F).setColor(red, green, blue, startAlpha); + BufferUploader.drawWithShader(buffer.buildOrThrow()); + } + + public int scrollHeight() { + int totalHeight = totalHeight(); + int listHeight = height - BOTTOM - TOP; + if (totalHeight <= listHeight) { + return 0; + } + return totalHeight - listHeight; + } + + public int totalHeight() { + int i = 8; + for (Option option : options) { + i += option.height(); + } + return i; + } + + public boolean hasErrors() { + for (Option option : options) { + if (option.hasErrors) { + return true; + } + } + return false; + } + + public boolean isEdited() { + for (Option option : options) { + if (option.isEdited()) { + return true; + } + } + return false; + } + + public void save() { + for (Option option : options) { + option.save(); + option.originalValue = option.value; + } + } + + @Override + public void onClose() { + if (isEdited()) { + minecraft.setScreen(new ConfirmScreen(this::acceptConfirm, Component.translatable("t.clc.quit_config"), + Component.translatable("t.clc.quit_config_sure"), + Component.translatable("t.clc.quit_discard"), + Component.translatable("gui.cancel"))); + } else { + minecraft.setScreen(parent); + } + } + + @Override + public boolean mouseScrolled(double d, double e, double f, double g) { + if (e >= TOP && e <= height - BOTTOM) { + scrollerAmount = Mth.clamp(scrollerAmount - f * 16.0D, 0, scrollHeight()); + return true; + } + return super.mouseScrolled(d, e, f, g); + } + + @Override + public boolean mouseClicked(double d, double e, int i) { + this.dragging = i == 0 && d >= width - 6 && d < width; + return super.mouseClicked(d, e, i) || dragging; + } + + @Override + public boolean mouseDragged(double d, double e, int i, double f, double g) { + if (super.mouseDragged(d, e, i, f, g)) { + return true; + } + if (i != 0 || !this.dragging) { + return false; + } + if (e < TOP) { + scrollerAmount = 0; + } else if (e > height - BOTTOM) { + scrollerAmount = scrollHeight(); + } else { + double h = Math.max(1, this.scrollHeight()); + int j = height - BOTTOM - TOP; + int k = Mth.clamp((int) ((float) (j * j) / (float) this.scrollHeight()), 32, j - 8); + double l = Math.max(1.0, h / (double) (j - k)); + scrollerAmount = Mth.clamp(scrollerAmount + g * l, 0, scrollHeight()); + } + return true; + } + + private void acceptConfirm(boolean t) { + if (!t) { + minecraft.setScreen(this); + } else { + minecraft.setScreen(parent); + } + } + + private void renderConfigTooltip(GuiGraphics stack, Font font, int mouseX, int mouseY, int startX, int startY, int sizeX, int sizeY, String title, String... description) { + if (mouseX > startX && mouseX < startX + sizeX) { + if (mouseY > startY && mouseY < startY + sizeY) { + List list = new ArrayList<>(); + list.add(Component.translatable(ChatFormatting.BOLD + "" + ChatFormatting.YELLOW + title)); + for (String desc : description) { + list.add(Component.translatable(desc)); + } + stack.renderComponentTooltip(font, list, mouseX, mouseY); + } + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java new file mode 100644 index 0000000..ed34f6b --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/AbstractConfigWidget.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.EditBox; + +/** + * Copied from Cloth Config Lite + * ... + */ +public class AbstractConfigWidget extends BaseWidget { + + public static final int buttonWidth = 200; + public static final int buttonHeight = 20; + public W widget; + + @Override + public void render(Minecraft minecraft, Font font, int x, int y, int width, int height, GuiGraphics matrices, int mouseX, int mouseY, float delta) { + super.render(minecraft, font, x, y, width, height, matrices, mouseX, mouseY, delta); + int i = (widget instanceof EditBox ? 1 : 0); + widget.setX(x + width - 200 - resetButtonOffset + i); + widget.setY(y + i + 1); + widget.render(matrices, mouseX, mouseY, delta); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java new file mode 100644 index 0000000..0583e61 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/BaseWidget.java @@ -0,0 +1,61 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextColor; + +/** + * Copied from Cloth Config Lite + * ... + */ +public class BaseWidget extends Option { + + public static final int resetButtonOffset = 48; + private final Button resetButton = addChild(Button.builder(Component.literal("Reset"), this::onResetPressed).size(46, 20).build()); + private boolean hideReset = false; + + private boolean isSubConfig = false; + + private void onResetPressed(Button button) { + value = defaultValue.get(); + reset(); + } + + public void hideReset() { + this.hideReset = true; + } + + public boolean isSubConfig() { + return isSubConfig; + } + + public void setSubConfig(boolean subConfig) { + isSubConfig = subConfig; + } + + @Override + public void render(Minecraft minecraft, Font font, int x, int y, int width, int height, GuiGraphics matrices, int mouseX, int mouseY, float delta) { + MutableComponent text = Component.literal(this.text.getString()); + boolean edited = isEdited() || hasErrors; + if (edited) { + text.withStyle(ChatFormatting.ITALIC); + if (hasErrors) { + text.withStyle(style -> style.withColor(TextColor.fromRgb(16733525))); + } + } else { + text.withStyle(ChatFormatting.GRAY); + } + matrices.drawString(font, text, x, y + font.lineHeight - 2, 0xFFFFFF); + resetButton.setX(x + width - 46); + resetButton.setY(y + 1); + resetButton.active = isNotDefault(); + if (!hideReset) { + resetButton.render(matrices, mouseX, mouseY, delta); + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java new file mode 100644 index 0000000..2478c7d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/InternalConfigButton.java @@ -0,0 +1,51 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractButton; +import net.minecraft.client.gui.narration.NarratedElementType; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.network.chat.Component; + +/** + * @author HypherionSA + */ +public class InternalConfigButton extends AbstractButton { + + CraterConfigScreen screen; + boolean cancel; + + public InternalConfigButton(CraterConfigScreen screen, int i, int j, int k, int l, Component component, boolean cancel) { + super(i, j, k, l, component); + this.screen = screen; + this.cancel = cancel; + } + + @Override + protected void renderWidget(GuiGraphics arg, int i, int j, float f) { + if (cancel) { + setMessage(Component.translatable(screen.isEdited() ? "t.clc.cancel_discard" : "gui.cancel")); + } else { + boolean hasErrors = screen.hasErrors(); + active = screen.isEdited() && !hasErrors; + setMessage(Component.translatable(hasErrors ? "t.clc.error" : "t.clc.save")); + } + super.renderWidget(arg, i, j, f); + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { + narrationElementOutput.add(NarratedElementType.USAGE, getMessage()); + } + + @Override + public void onPress() { + if (cancel) { + screen.onClose(); + } else { + screen.save(); + } + } + + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java new file mode 100644 index 0000000..f49ca68 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/Option.java @@ -0,0 +1,71 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import lombok.Getter; +import lombok.Setter; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Copied from Cloth Config Lite + * ... + */ +public abstract class Option extends AbstractContainerEventHandler { + + public Component text; + @Nullable + public Supplier defaultValue; + public Consumer savingConsumer; + public T originalValue; + public T value; + public boolean hasErrors; + public List children = new ArrayList<>(); + @Setter + @Getter + private List langKeys = new ArrayList<>(); + + public abstract void render(Minecraft minecraft, Font font, int x, int y, int width, int height, GuiGraphics matrices, int mouseX, int mouseY, float delta); + + public int height() { + return 22; + } + + @Override + public List children() { + return children; + } + + protected R addChild(R listener) { + ((List) children).add(listener); + return listener; + } + + public void onAdd() { + } + + protected void reset() { + onAdd(); + } + + public boolean isEdited() { + return !Objects.equals(originalValue, value); + } + + protected boolean isNotDefault() { + return defaultValue != null && !Objects.equals(defaultValue.get(), value); + } + + public void save() { + savingConsumer.accept(value); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java new file mode 100644 index 0000000..056f32c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/SubConfigWidget.java @@ -0,0 +1,40 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen; +import com.hypherionmc.craterlib.core.config.AbstractConfig; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; + +/** + * @author HypherionSA + */ +public class SubConfigWidget extends AbstractConfigWidget { + + private final Object subConfig; + private final AbstractConfig config; + private final Screen screen; + + public SubConfigWidget(AbstractConfig config, Screen screen, Object subConfig) { + this.config = config; + this.subConfig = subConfig; + this.screen = screen; + + this.widget = addChild(Button.builder(Component.translatable("t.clc.opensubconfig"), this::openSubConfig).size(200, buttonHeight).build()); + } + + @Override + public void render(Minecraft minecraft, Font font, int x, int y, int width, int height, GuiGraphics matrices, int mouseX, int mouseY, float delta) { + this.text = Component.literal(subConfig.getClass().getSimpleName().toLowerCase()); + this.hideReset(); + super.render(minecraft, font, x, y, width, height, matrices, mouseX, mouseY, delta); + } + + private void openSubConfig(Button button) { + Minecraft.getInstance().setScreen(new CraterConfigScreen(config, screen, subConfig)); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java new file mode 100644 index 0000000..cc05cec --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/TextConfigOption.java @@ -0,0 +1,45 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; + +import java.util.function.Function; + +/** + * Copied from Cloth Config Lite + * ... + */ +public class TextConfigOption extends AbstractConfigWidget { + + private final Function toString; + private final Function fromString; + + public TextConfigOption(Function toString, Function fromString) { + this.toString = toString; + this.fromString = fromString; + this.widget = addChild(new WrappedEditBox(Minecraft.getInstance().font, 0, 0, 198, 18, null)); + } + + @Override + public void onAdd() { + widget.setMaxLength(1000000); + widget.setValue(toString.apply(value)); + widget.setResponder(this::update); + } + + @Override + public void render(Minecraft minecraft, Font font, int x, int y, int width, int height, GuiGraphics matrices, int mouseX, int mouseY, float delta) { + widget.setTextColor(hasErrors ? 16733525 : 14737632); + super.render(minecraft, font, x, y, width, height, matrices, mouseX, mouseY, delta); + } + + private void update(String s) { + try { + this.value = fromString.apply(s); + this.hasErrors = false; + } catch (Exception e) { + this.hasErrors = true; + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java new file mode 100644 index 0000000..03ea11d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/ToggleButton.java @@ -0,0 +1,33 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; + +import java.util.List; +import java.util.function.Function; + +/** + * Copied from Cloth Config Lite + * ... + */ +public class ToggleButton extends AbstractConfigWidget { + + private final List options; + private final Function toComponent; + + public ToggleButton(List options, Function toComponent) { + this.options = options; + this.toComponent = toComponent; + this.widget = addChild(Button.builder(Component.empty(), this::switchNext).size(buttonWidth, buttonHeight).build()); + } + + @Override + public void onAdd() { + widget.setMessage(toComponent.apply(value)); + } + + private void switchNext(Button button) { + value = options.get((options.indexOf(value) + 1) % options.size()); + onAdd(); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java new file mode 100644 index 0000000..42c0c0c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/gui/config/widgets/WrappedEditBox.java @@ -0,0 +1,29 @@ +package com.hypherionmc.craterlib.client.gui.config.widgets; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.NotNull; + +/** + * @author HypherionSA + */ +public class WrappedEditBox extends EditBox { + + public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) { + super(font, i, j, k, l, component); + } + + @Override + public void setFocused(boolean bl) { + for (GuiEventListener child : Minecraft.getInstance().screen.children()) { + if (child instanceof TextConfigOption option) { + WrappedEditBox box = option.widget; + super.setFocused(box == this); + } + } + super.setFocused(bl); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionCondition.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionCondition.java new file mode 100644 index 0000000..e8bffde --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionCondition.java @@ -0,0 +1,13 @@ +package com.hypherionmc.craterlib.client.mentions; + +/** + * Based on ... + */ +@FunctionalInterface +public interface MentionCondition { + + boolean shouldAddMention(String currentWord); + + MentionCondition ALWAYS = currentWord -> true; + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionsController.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionsController.java new file mode 100644 index 0000000..bad3b43 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/client/mentions/MentionsController.java @@ -0,0 +1,47 @@ +package com.hypherionmc.craterlib.client.mentions; + +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Based on ... + */ +public class MentionsController { + + private static final Map> mentions = new LinkedHashMap<>(); + private static final Map mentionConditions = new LinkedHashMap<>(); + @Getter + private static boolean lastMentionConditional = true; + + public static void registerMention(ResourceIdentifier mentionClass, Collection suggestions, MentionCondition condition) { + mentions.put(mentionClass, suggestions); + mentionConditions.put(mentionClass, condition); + } + + public static Collection getMentions(String currentWord) { + ArrayList applicableMentions = new ArrayList<>(); + lastMentionConditional = false; + + mentionConditions.forEach((mention, condition) -> { + boolean shouldSuggest = condition.shouldAddMention(currentWord); + if (!shouldSuggest) return; + + if (!lastMentionConditional && condition != MentionCondition.ALWAYS) { + lastMentionConditional = true; + } + + applicableMentions.addAll(mentions.get(mention)); + }); + + return applicableMentions; + } + + public static boolean hasMentions() { + return !mentions.isEmpty(); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/compat/FTBEssentials.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/compat/FTBEssentials.java new file mode 100644 index 0000000..bf52038 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/compat/FTBEssentials.java @@ -0,0 +1,16 @@ +package com.hypherionmc.craterlib.compat; + +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import dev.ftb.mods.ftbessentials.util.FTBEPlayerData; + +import java.util.Optional; + +public class FTBEssentials { + + public static boolean isPlayerMuted(BridgedPlayer player) { + Optional data = FTBEPlayerData.getOrCreate(player.toMojang()); + return data.map(FTBEPlayerData::isMuted).orElse(false); + + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/compat/LuckPermsCompat.java new file mode 100644 index 0000000..a77e1f4 --- /dev/null +++ b/1.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/AbstractConfig.java new file mode 100644 index 0000000..eceac79 --- /dev/null +++ b/1.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java new file mode 100644 index 0000000..760ca93 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ConfigController.java @@ -0,0 +1,61 @@ +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; +import java.util.HashMap; + +/** + * @author HypherionSA + * Controls Config File Reloads and Events + */ +public final class ConfigController implements Serializable { + + /** + * Cache of registered configs + */ + @Getter + private static final HashMap> watchedConfigs = new HashMap<>(); + + /** + * INTERNAL METHOD - Register and watch the config + * + * @param config - The config class to register and watch + */ + @ApiStatus.Internal + @Deprecated + public static void register_config(ModuleConfig config) { + 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.isWasSaveCalled()) { + CraterConstants.LOG.info("Sending Reload Event for: {}", config.getConfigPath().getName()); + config.configReloaded(); + } + }); + } catch (Exception e) { + CraterConstants.LOG.error("Failed to register {} for auto reloading. {}", config.getConfigPath().getName(), e.getMessage()); + } + watchedConfigs.put(config.getConfigPath().toString(), Pair.of(config, configWatcher)); + CraterConstants.LOG.info("Registered {} successfully!", config.getConfigPath().getName()); + } + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java new file mode 100644 index 0000000..6e36aed --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/ModuleConfig.java @@ -0,0 +1,120 @@ +package com.hypherionmc.craterlib.core.config; + +import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; +import me.hypherionmc.moonconfig.core.CommentedConfig; + +import java.io.File; + +/** + * @author HypherionSA + * Base Config class containing the save, upgrading and loading logic. + * All config classes must extend this class + */ +@Deprecated(forRemoval = true, since = "2.1.0") +public class ModuleConfig extends AbstractConfig { + + /** + * Set up the config + * + * @param modId - The ID of the Mod/Module the config belongs to + * @param configName - The name of the config file, excluding extension + */ + public ModuleConfig(String modId, String configName) { + this(modId, "", configName); + } + + public ModuleConfig(String modId, String subFolder, String configName) { + super(modId, subFolder.isEmpty() ? null : subFolder, configName); + } + + /** + * This method has to be called in the config constructor. This creates or upgrades the config file as needed + * + * @param config - The config class to use + */ + public void registerAndSetup(ModuleConfig config) { + super.registerAndSetup(config); + } + + /** + * Save the config to the disk + * + * @param conf - The config class to serialize and save + */ + public void saveConfig(ModuleConfig conf) { + super.registerAndSetup(conf); + } + + /** + * Load the config from the file into a Class + * + * @param conf - The config Class to load + * @return - Returns the loaded version of the class + */ + public T loadConfig(Object conf) { + return (T) super.readConfig(conf); + } + + /** + * INTERNAL METHOD - Upgrades the config files in the events the config structure changes + * + * @param conf - The config class to load + */ + public void migrateConfig(ModuleConfig conf) { + super.migrateConfig(conf); + } + + public void updateConfigValues(CommentedConfig oldConfig, CommentedConfig newConfig, CommentedConfig outputConfig, String subKey) { + if (getConfigFormat() instanceof TomlConfigFormat t) { + t.updateConfigValues(oldConfig, newConfig, outputConfig, subKey); + } + } + + /** + * Get the location of the config file + * + * @return - The FILE object containing the config file + */ + public File getConfigPath() { + return super.getConfigPath(); + } + + /** + * Get the NETWORK SYNC ID + * + * @return - Returns the Sync ID in format modid:config_name + */ + public String getNetworkID() { + return super.getNetworkID(); + } + + /** + * Fired whenever changes to the config are detected + */ + @Override + public void configReloaded() { + + } + + /** + * Get the name of the Config File + * + * @return + */ + public String getConfigName() { + return super.getConfigName(); + } + + /** + * Get the MODID of the Module the config is registered to + * + * @return + */ + public String getModId() { + return super.getModId(); + } + + public boolean isSaveCalled() { + return super.isWasSaveCalled(); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/HideFromScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/HideFromScreen.java new file mode 100644 index 0000000..512a025 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/HideFromScreen.java @@ -0,0 +1,8 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface HideFromScreen { +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/NoConfigScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/NoConfigScreen.java new file mode 100644 index 0000000..4288ee0 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/NoConfigScreen.java @@ -0,0 +1,12 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author HypherionSA + * Allows Modules to disable Automatic Config Screens + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface NoConfigScreen { +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/SubConfig.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/SubConfig.java new file mode 100644 index 0000000..e3ec808 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/SubConfig.java @@ -0,0 +1,16 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author HypherionSA + * Used to determine if a Config section should be rendered as a separate screen + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SubConfig { +} + diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Syncable.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Syncable.java new file mode 100644 index 0000000..838b35b --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Syncable.java @@ -0,0 +1,15 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author HypherionSA + * //TODO Currently unused, but to be used with Config Syncing in the future + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Syncable { +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Tooltip.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Tooltip.java new file mode 100644 index 0000000..538471c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/annotations/Tooltip.java @@ -0,0 +1,16 @@ +package com.hypherionmc.craterlib.core.config.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author HypherionSA + * Provides tooltips to the config GUI + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Tooltip { + String[] value(); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/AbstractConfigFormat.java new file mode 100644 index 0000000..20511ba --- /dev/null +++ b/1.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/JsonConfigFormat.java new file mode 100644 index 0000000..461890b --- /dev/null +++ b/1.21.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()).sync().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.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/config/formats/TomlConfigFormat.java new file mode 100644 index 0000000..dfe4527 --- /dev/null +++ b/1.21.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()).sync().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.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEvent.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEvent.java new file mode 100644 index 0000000..89ff00a --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEvent.java @@ -0,0 +1,30 @@ +package com.hypherionmc.craterlib.core.event; + +import com.hypherionmc.craterlib.core.event.annot.Cancellable; +import com.hypherionmc.craterlib.core.event.exception.CraterEventCancellationException; + +public class CraterEvent { + + private boolean canceled = false; + + private boolean canCancel() { + return this.getClass().isAnnotationPresent(Cancellable.class); + } + + public void cancelEvent() { + try { + if (!this.canCancel()) { + throw new CraterEventCancellationException("Tried to cancel non-cancelable event: " + this.getClass().getName()); + } + + this.canceled = true; + } catch (Exception e) { + e.printStackTrace(); + } + } + + public boolean wasCancelled() { + return this.canceled; + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventBus.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventBus.java new file mode 100644 index 0000000..74eeecc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventBus.java @@ -0,0 +1,241 @@ +package com.hypherionmc.craterlib.core.event; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.core.event.annot.CraterEventListener; +import org.slf4j.Logger; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.function.Consumer; + +public final class CraterEventBus { + + public static final CraterEventBus INSTANCE = new CraterEventBus(); + private static final Logger LOGGER = CraterConstants.LOG; + private final Map, List> events = new HashMap<>(); + + public void postEvent(CraterEvent event) { + if (eventsRegisteredForType(event.getClass())) { + List l = new ArrayList<>(events.get(event.getClass())); + l.sort((o1, o2) -> Integer.compare(o2.priority, o1.priority)); + + for (ListenerContainer c : l) { + c.notifyListener(event); + } + } + } + + public void registerEventListener(Class clazz) { + this.registerListenerMethods(this.getEventMethodsOf(clazz)); + } + + public void registerEventListener(Object object) { + this.registerListenerMethods(this.getEventMethodsOf(object)); + } + + private void registerListenerMethods(List methods) { + for (EventMethod m : methods) { + Consumer listener = (event) -> { + try { + m.method.invoke(m.parentObject, event); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + + ListenerContainer container = new ListenerContainer(m.eventType, listener, m.priority); + container.listenerParentClassName = m.parentClass.getName(); + container.listenerMethodName = m.method.getName(); + this.registerListener(container); + } + } + + private List getEventMethodsOf(Object objectOrClass) { + List l = new ArrayList<>(); + try { + if (objectOrClass != null) { + boolean isClass = (objectOrClass instanceof Class); + Class c = isClass ? (Class) objectOrClass : objectOrClass.getClass(); + for (Method m : c.getMethods()) { + if (isClass && Modifier.isStatic(m.getModifiers())) { + EventMethod em = EventMethod.tryCreateFrom(new AnalyzedMethod(m, c)); + if ((em != null) && this.hasEventAnnotation(em)) l.add(em); + } + if (!isClass && !Modifier.isStatic(m.getModifiers())) { + EventMethod em = EventMethod.tryCreateFrom(new AnalyzedMethod(m, objectOrClass)); + if ((em != null) && this.hasEventAnnotation(em)) l.add(em); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return l; + } + + private boolean hasEventAnnotation(EventMethod m) { + for (Annotation a : m.annotations) { + if (a instanceof CraterEventListener) return true; + } + return false; + } + + public void registerListener(Consumer listener, Class eventType) { + this.registerListener(listener, eventType, 0); + } + + public void registerListener(Consumer listener, Class eventType, int priority) { + this.registerListener(new ListenerContainer(eventType, listener, priority)); + } + + private void registerListener(ListenerContainer listenerContainer) { + try { + if (!eventsRegisteredForType(listenerContainer.eventType)) { + events.put(listenerContainer.eventType, new ArrayList<>()); + } + events.get(listenerContainer.eventType).add(listenerContainer); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public boolean eventsRegisteredForType(Class eventType) { + if (eventType == null) { + return false; + } + return this.events.containsKey(eventType); + } + + protected final static class ListenerContainer { + + private final Consumer listener; + private final Class eventType; + private final int priority; + private String listenerParentClassName = "[unknown]"; + private String listenerMethodName = "[unknown]"; + + private ListenerContainer(Class eventType, Consumer listener, int priority) { + this.eventType = eventType; + this.listener = listener; + this.priority = priority; + } + + private void notifyListener(CraterEvent event) { + try { + this.listener.accept(event); + } catch (Exception e) { + LOGGER.error("##################################"); + LOGGER.error("Failed to notify event listener!"); + LOGGER.error("Event Type: " + this.eventType.getName()); + LOGGER.error("Listener Parent Class Name: " + this.listenerParentClassName); + LOGGER.error("Listener Method Name In Parent Class: " + this.listenerMethodName); + LOGGER.error("##################################"); + e.printStackTrace(); + } + } + } + + protected static class AnalyzedMethod { + + protected Method method; + protected Object parentObject; + protected Class parentClass; + protected boolean isStatic; + protected List annotations = new ArrayList<>(); + + protected AnalyzedMethod() { + } + + protected AnalyzedMethod(Method method, Object parentObjectOrClass) { + this.method = method; + this.parentObject = parentObjectOrClass; + this.parentClass = this.tryGetParentClass(); + this.isStatic = Modifier.isStatic(method.getModifiers()); + collectMethodAnnotations(this.isStatic ? null : this.parentObject.getClass(), this.method, this.annotations); + } + + protected static void collectMethodAnnotations(Class c, Method m, List addToList) { + try { + addToList.addAll(Arrays.asList(m.getAnnotations())); + if (!Modifier.isStatic(m.getModifiers()) && (c != null)) { + Class sc = c.getSuperclass(); + if (sc != null) { + try { + Method sm = sc.getMethod(m.getName(), m.getParameterTypes()); + collectMethodAnnotations(sc, sm, addToList); + } catch (Exception ignored) { + } + } + } + } catch (Exception ignored) { + } + } + + protected Class tryGetParentClass() { + if (this.parentObject instanceof Class) { + return (Class) this.parentObject; + } + return this.parentObject.getClass(); + } + + } + + protected static class EventMethod extends AnalyzedMethod { + + protected final int priority; + protected final Class eventType; + + protected EventMethod(AnalyzedMethod method) { + + super(); + this.method = method.method; + this.parentObject = method.parentObject; + this.parentClass = method.parentClass; + this.isStatic = method.isStatic; + this.annotations = method.annotations; + + this.priority = this.tryGetPriority(); + this.eventType = this.tryGetEventType(); + + } + + protected static EventMethod tryCreateFrom(AnalyzedMethod method) { + EventMethod em = new EventMethod(method); + return (em.eventType != null) ? em : null; + } + + protected Class tryGetEventType() { + try { + if (this.method != null) { + Class[] params = this.method.getParameterTypes(); + if (params.length > 0) { + Class firstParam = params[0]; + if (CraterEvent.class.isAssignableFrom(firstParam)) { + return (Class) firstParam; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + protected int tryGetPriority() { + try { + for (Annotation a : this.annotations) { + if (a instanceof CraterEventListener craterEventListener) { + return craterEventListener.priority(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventPriority.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventPriority.java new file mode 100644 index 0000000..0c97134 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/CraterEventPriority.java @@ -0,0 +1,13 @@ +package com.hypherionmc.craterlib.core.event; + +public class CraterEventPriority { + + public static final int LOWEST = -3; + public static final int LOWER = -2; + public static final int LOW = -1; + public static final int NORMAL = 0; + public static final int HIGH = 1; + public static final int HIGHER = 2; + public static final int HIGHEST = 3; + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/Cancellable.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/Cancellable.java new file mode 100644 index 0000000..ee3c2fc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/Cancellable.java @@ -0,0 +1,8 @@ +package com.hypherionmc.craterlib.core.event.annot; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Cancellable { +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/CraterEventListener.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/CraterEventListener.java new file mode 100644 index 0000000..7b42490 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/annot/CraterEventListener.java @@ -0,0 +1,11 @@ +package com.hypherionmc.craterlib.core.event.annot; + +import com.hypherionmc.craterlib.core.event.CraterEventPriority; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CraterEventListener { + int priority() default CraterEventPriority.NORMAL; +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/exception/CraterEventCancellationException.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/exception/CraterEventCancellationException.java new file mode 100644 index 0000000..fe2a8a3 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/exception/CraterEventCancellationException.java @@ -0,0 +1,9 @@ +package com.hypherionmc.craterlib.core.event.exception; + +public class CraterEventCancellationException extends Exception { + + public CraterEventCancellationException(String msg) { + super(msg); + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/package-info.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/package-info.java new file mode 100644 index 0000000..5ec1749 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/event/package-info.java @@ -0,0 +1,5 @@ +/** + * The event system code in this package is based on, and adapted from Acara (https://github.com/Keksuccino/acara/) + * and is licensed under MIT by Keksuccino + */ +package com.hypherionmc.craterlib.core.event; \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/CraterPacketNetwork.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/CraterPacketNetwork.java new file mode 100644 index 0000000..081c8fb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/CraterPacketNetwork.java @@ -0,0 +1,43 @@ +package com.hypherionmc.craterlib.core.networking; + +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; +import lombok.Getter; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +@Getter +public class CraterPacketNetwork { + + private final PacketRegistry packetRegistry; + public static CraterPacketNetwork INSTANCE; + private static DeferredPacketRegistrar delayedHandler; + + public CraterPacketNetwork(PacketRegistry registry) { + INSTANCE = this; + this.packetRegistry = registry; + getDelayedHandler().registerQueuedPackets(registry); + } + + private static DeferredPacketRegistrar getDelayedHandler() { + if (delayedHandler == null) { + delayedHandler = new DeferredPacketRegistrar(); + } + return delayedHandler; + } + + public static PacketRegistrar registerPacket(ResourceIdentifier id, Class messageType, BiConsumer encoder, Function decoder, Consumer> handler) { + if (INSTANCE != null) { + return INSTANCE.packetRegistry.registerPacket(id, messageType, encoder, decoder, handler); + } else { + return getDelayedHandler().registerPacket(id, messageType, encoder, decoder, handler); + } + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/DeferredPacketRegistrar.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/DeferredPacketRegistrar.java new file mode 100644 index 0000000..25556f4 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/DeferredPacketRegistrar.java @@ -0,0 +1,41 @@ +package com.hypherionmc.craterlib.core.networking; + +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public class DeferredPacketRegistrar implements PacketRegistrar { + + private static final Map, PacketHolder> QUEUED_PACKET_MAP = new HashMap<>(); + + @Override + public PacketSide side() { + return PacketSide.CLIENT; + } + + @Override + public PacketRegistrar registerPacket(ResourceIdentifier packetIdentifier, Class messageType, BiConsumer encoder, Function decoder, Consumer> handler) { + PacketHolder container = new PacketHolder<>(packetIdentifier, messageType, encoder, decoder, handler); + QUEUED_PACKET_MAP.put(messageType, container); + return this; + } + + + public void registerQueuedPackets(PacketRegistry packetRegistration) { + if (!QUEUED_PACKET_MAP.isEmpty()) { + packetRegistration.PACKET_MAP.putAll(QUEUED_PACKET_MAP); + QUEUED_PACKET_MAP.forEach((aClass, container) -> packetRegistration.registerPacket(container)); + } + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistrar.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistrar.java new file mode 100644 index 0000000..3ea55fa --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistrar.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.core.networking; + +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public interface PacketRegistrar { + + PacketSide side(); + + PacketRegistrar registerPacket(ResourceIdentifier id, Class messageType, BiConsumer encoder, Function decoder, Consumer> handler); + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistry.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistry.java new file mode 100644 index 0000000..d1ec06c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/PacketRegistry.java @@ -0,0 +1,41 @@ +package com.hypherionmc.craterlib.core.networking; + +import com.hypherionmc.craterlib.api.networking.CraterNetworkHandler; +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public abstract class PacketRegistry implements CraterNetworkHandler, PacketRegistrar { + + protected final Map, PacketHolder> PACKET_MAP = new HashMap<>(); + + protected final PacketSide side; + + public PacketRegistry(PacketSide side) { + this.side = side; + } + + public PacketRegistrar registerPacket(ResourceIdentifier id, Class messageType, BiConsumer encoder, Function decoder, Consumer> handler) { + PacketHolder holder = new PacketHolder<>(id, messageType, encoder, decoder, handler); + PACKET_MAP.put(messageType, holder); + registerPacket(holder); + return this; + } + + public PacketSide side() { + return side; + } + + protected abstract void registerPacket(PacketHolder packetHolder); +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketContext.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketContext.java new file mode 100644 index 0000000..fc432dc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketContext.java @@ -0,0 +1,15 @@ +package com.hypherionmc.craterlib.core.networking.data; + +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import org.jetbrains.annotations.Nullable; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public record PacketContext(@Nullable BridgedPlayer sender, T message, PacketSide side) { + + public PacketContext(T message, PacketSide side) { + this(null, message, side); + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketHolder.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketHolder.java new file mode 100644 index 0000000..10873c5 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketHolder.java @@ -0,0 +1,40 @@ +package com.hypherionmc.craterlib.core.networking.data; + +import com.hypherionmc.craterlib.api.networking.CommonPacketWrapper; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public record PacketHolder(CustomPacketPayload.Type type, + Class messageType, + BiConsumer encoder, + Function decoder, + Consumer> handler) { + + public PacketHolder(ResourceIdentifier packetId, Class messageType, BiConsumer encoder, Function decoder, Consumer> handler) { + this(new CustomPacketPayload.Type<>(packetId.toMojang()), messageType, encoder, decoder, handler); + } + + @SuppressWarnings("unchecked") + public CustomPacketPayload.Type getType() + { + return (CustomPacketPayload.Type) type(); + } + + public StreamCodec getCodec() + { + return CustomPacketPayload.codec( + (packet, buf) -> this.encoder().accept((T)packet.packet(), BridgedFriendlyByteBuf.of(buf)), + (buf) -> new CommonPacketWrapper<>(this, this.decoder().apply(BridgedFriendlyByteBuf.of(buf)))); + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketSide.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketSide.java new file mode 100644 index 0000000..ad6f8ec --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/networking/data/PacketSide.java @@ -0,0 +1,13 @@ +package com.hypherionmc.craterlib.core.networking.data; + +public enum PacketSide { + CLIENT, + SERVER; + + public PacketSide flipped() { + if (CLIENT.equals(this)) + return SERVER; + + return CLIENT; + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ClientPlatform.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ClientPlatform.java new file mode 100644 index 0000000..089fb36 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ClientPlatform.java @@ -0,0 +1,23 @@ +package com.hypherionmc.craterlib.core.platform; + +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.InternalServiceUtil; +import net.minecraft.network.Connection; + +/** + * @author HypherionSA + */ +public interface ClientPlatform { + + public final ClientPlatform INSTANCE = InternalServiceUtil.load(ClientPlatform.class); + + BridgedMinecraft getClientInstance(); + + BridgedPlayer getClientPlayer(); + + BridgedClientLevel getClientLevel(); + + Connection getClientConnection(); +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CommonPlatform.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CommonPlatform.java new file mode 100644 index 0000000..ee7f944 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CommonPlatform.java @@ -0,0 +1,15 @@ +package com.hypherionmc.craterlib.core.platform; + +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import com.hypherionmc.craterlib.utils.InternalServiceUtil; + +/** + * @author HypherionSA + */ +public interface CommonPlatform { + + public CommonPlatform INSTANCE = InternalServiceUtil.load(CommonPlatform.class); + + BridgedMinecraftServer getMCServer(); + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CompatUtils.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CompatUtils.java new file mode 100644 index 0000000..fb745fc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/CompatUtils.java @@ -0,0 +1,13 @@ +package com.hypherionmc.craterlib.core.platform; + +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.InternalServiceUtil; + +public interface CompatUtils { + + public static final CompatUtils INSTANCE = InternalServiceUtil.load(CompatUtils.class); + + boolean isPlayerActive(BridgedPlayer player); + String getSkinUUID(BridgedPlayer player); + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/Environment.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/Environment.java new file mode 100644 index 0000000..2ef219e --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/Environment.java @@ -0,0 +1,18 @@ +package com.hypherionmc.craterlib.core.platform; + +/** + * @author HypherionSA + */ +public enum Environment { + CLIENT, + SERVER, + UNKNOWN; + + public boolean isClient() { + return this == CLIENT; + } + + public boolean isServer() { + return this == SERVER; + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/LoaderType.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/LoaderType.java new file mode 100644 index 0000000..c268a01 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/LoaderType.java @@ -0,0 +1,8 @@ +package com.hypherionmc.craterlib.core.platform; + +public enum LoaderType { + FABRIC, + FORGE, + NEOFORGE, + PAPER +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ModloaderEnvironment.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ModloaderEnvironment.java new file mode 100644 index 0000000..4890b8a --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/platform/ModloaderEnvironment.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.core.platform; + +import com.hypherionmc.craterlib.utils.InternalServiceUtil; + +import java.io.File; + +/** + * @author HypherionSA + * Helper class to provide information about the ModLoader + */ +public interface ModloaderEnvironment { + + public final ModloaderEnvironment INSTANCE = InternalServiceUtil.load(ModloaderEnvironment.class); + + @Deprecated(forRemoval = true, since = "2.0.2") + boolean isFabric(); + + LoaderType getLoaderType(); + + String getGameVersion(); + + File getGameFolder(); + + File getConfigFolder(); + + File getModsFolder(); + + Environment getEnvironment(); + + boolean isModLoaded(String modid); + + boolean isDevEnv(); + + int getModCount(); +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordEventHandlers.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordEventHandlers.java new file mode 100644 index 0000000..3f1ffcb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordEventHandlers.java @@ -0,0 +1,90 @@ +package com.hypherionmc.craterlib.core.rpcsdk; + +import com.hypherionmc.craterlib.core.rpcsdk.callbacks.*; +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * @author HypherionSA + * Class containing references to all available discord event handles. + * Registering a handler is optional, and non-assigned handlers will be ignored + */ +public class DiscordEventHandlers extends Structure { + + // Callback for when the RPC was initialized successfully + public ReadyCallback ready; + + // Callback for when the Discord connection was ended + public DisconnectedCallback disconnected; + + // Callback for when a Discord Error occurs + public ErroredCallback errored; + + // Callback for when a player joins the game + public JoinGameCallback joinGame; + + // Callback for when a player spectates the game + public SpectateGameCallback spectateGame; + + // Callback for when a players request to join your game + public JoinRequestCallback joinRequest; + + /** + * DO NOT TOUCH THIS... EVER! + */ + @Override + protected List getFieldOrder() { + return Arrays.asList( + "ready", + "disconnected", + "errored", + "joinGame", + "spectateGame", + "joinRequest" + ); + } + + public static class Builder { + private final DiscordEventHandlers handlers; + + public Builder() { + this.handlers = new DiscordEventHandlers(); + } + + public Builder ready(ReadyCallback readyCallback) { + handlers.ready = readyCallback; + return this; + } + + public Builder disconnected(DisconnectedCallback disconnectedCallback) { + handlers.disconnected = disconnectedCallback; + return this; + } + + public Builder errored(ErroredCallback erroredCallback) { + handlers.errored = erroredCallback; + return this; + } + + public Builder joinGame(JoinGameCallback joinGameCallback) { + handlers.joinGame = joinGameCallback; + return this; + } + + public Builder spectateGame(SpectateGameCallback spectateGameCallback) { + handlers.spectateGame = spectateGameCallback; + return this; + } + + public Builder joinRequest(JoinRequestCallback joinRequestCallback) { + handlers.joinRequest = joinRequestCallback; + return this; + } + + public DiscordEventHandlers build() { + return handlers; + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRPC.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRPC.java new file mode 100644 index 0000000..7cf53ff --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRPC.java @@ -0,0 +1,99 @@ +package com.hypherionmc.craterlib.core.rpcsdk; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author HypherionSA + * Java Wrapper of the Discord-RPC Library + */ +public interface DiscordRPC extends Library { + + DiscordRPC INSTANCE = Native.load("discord-rpc", DiscordRPC.class); + + /** + * Open a New RPC Connection + * + * @param applicationId The ID of the Application the RPC is tied to + * @param handlers Optional Event Callback Handlers + * @param autoRegister Auto Register the running game + * @param steamId Steam ID of the game + */ + void Discord_Initialize(@NotNull String applicationId, @Nullable DiscordEventHandlers handlers, boolean autoRegister, @Nullable String steamId); + + /** + * Shutdown the RPC instance and disconnect from discord + */ + void Discord_Shutdown(); + + /** + * Need to be called manually at least every 2 seconds, to allow RPC updates + * and callback handlers to fire + */ + void Discord_RunCallbacks(); + + /** + * Not sure about this. Believe it needs to be called manually in some circumstances + */ + void Discord_UpdateConnection(); + + /** + * Update the Rich Presence + * + * @param struct Constructed {@link DiscordRichPresence} + */ + void Discord_UpdatePresence(@Nullable DiscordRichPresence struct); + + /** + * Clear the current Rich Presence + */ + void Discord_ClearPresence(); + + /** + * Respond to Join/Spectate callback + * + * @param userid The Discord User ID of the user that initiated the request + * @param reply Reply to the request. See {@link DiscordReply} + */ + void Discord_Respond(@NotNull String userid, int reply); + + /** + * Replace the already registered {@link DiscordEventHandlers} + * + * @param handlers The new handlers to apply + */ + void Discord_UpdateHandlers(@Nullable DiscordEventHandlers handlers); + + /** + * Register the executable of the application/game + * Only applicable when autoRegister is set to false + * + * @param applicationId The Application ID + * @param command The Launch command of the game + *

+ * NB: THIS DOES NOT WORK WITH MINECRAFT + */ + void Discord_Register(String applicationId, String command); + + /** + * Register the Steam executable of the application/game + * + * @param applicationId The Application ID + * @param steamId The Steam ID of the application/game + */ + void Discord_RegisterSteamGame(String applicationId, String steamId); + + public enum DiscordReply { + NO(0), + YES(1), + IGNORE(2); + + public final int reply; + + DiscordReply(int reply) { + this.reply = reply; + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRichPresence.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRichPresence.java new file mode 100644 index 0000000..51fd720 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordRichPresence.java @@ -0,0 +1,248 @@ +package com.hypherionmc.craterlib.core.rpcsdk; + +import com.hypherionmc.craterlib.core.rpcsdk.helpers.RPCButton; +import com.sun.jna.Structure; +import org.jetbrains.annotations.NotNull; + +import java.time.OffsetDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author HypherionSA + * Class reprenting a Discord RPC activity + */ +public class DiscordRichPresence extends Structure { + + // First line of text on the RPC + public String state; + + // Second line of text on the RPC + public String details; + + // Time the activity started in UNIX-Timestamp format + public long startTimestamp; + + // Time the activity will end in UNIX-Timestamp format + public long endTimestamp; + + // URL or Asset key of the Large Image + public String largeImageKey; + + // Hover text to display when hovering the Large Image + public String largeImageText; + + // URL or Asset key of the Small Image + public String smallImageKey; + + // Hover text to display when hovering the Small Image + public String smallImageText; + + // Id of the player's party, lobby, or group. + public String partyId; + + // Current size of the player's party, lobby, or group. + public int partySize; + + // Maximum size of the player's party, lobby, or group. + public int partyMax; + + // Unused + public String partyPrivacy; + + // Unused. + public String matchSecret; + + // Unique hashed string for chat invitations and Ask to Join. + public String joinSecret; + + // Unique hashed string for Spectate button. + public String spectateSecret; + + // Label of the First RPC Button + public String button_label_1; + + // URL of the First RPC Button + public String button_url_1; + + // Label of the Second RPC Button + public String button_label_2; + + // URL of the Second RPC Button + public String button_url_2; + + // Unused + public int instance; + + public DiscordRichPresence() { + setStringEncoding("UTF-8"); + } + + /** + * DO NOT TOUCH THIS... EVER! + */ + @Override + protected List getFieldOrder() { + return Arrays.asList( + "state", + "details", + "startTimestamp", + "endTimestamp", + "largeImageKey", + "largeImageText", + "smallImageKey", + "smallImageText", + "partyId", + "partySize", + "partyMax", + "partyPrivacy", + "matchSecret", + "joinSecret", + "spectateSecret", + "button_label_1", + "button_url_1", + "button_label_2", + "button_url_2", + "instance" + ); + } + + public static class Builder { + private final DiscordRichPresence rpc; + + public Builder(String state) { + rpc = new DiscordRichPresence(); + + if (state != null && !state.isEmpty()) { + rpc.state = state.substring(0, Math.min(state.length(), 128)); + } + } + + public Builder setDetails(String details) { + if (details != null && !details.isEmpty()) { + rpc.details = details.substring(0, Math.min(details.length(), 128)); + } + return this; + } + + public Builder setStartTimestamp(long timestamp) { + rpc.startTimestamp = timestamp; + return this; + } + + public Builder setStartTimestamp(OffsetDateTime timestamp) { + rpc.startTimestamp = timestamp.toEpochSecond(); + return this; + } + + public Builder setEndTimestamp(long timestamp) { + rpc.endTimestamp = timestamp; + return this; + } + + public Builder setEndTimestamp(OffsetDateTime timestamp) { + rpc.endTimestamp = timestamp.toEpochSecond(); + return this; + } + + public Builder setLargeImage(String key) { + return this.setLargeImage(key, ""); + } + + public Builder setLargeImage(@NotNull String key, String text) { + // Null check used for users blatantly ignoring the NotNull marker + if ((text != null && !text.isEmpty()) && key != null) { + throw new IllegalArgumentException("Image key cannot be null when assigning a hover text"); + } + + rpc.largeImageKey = key; + rpc.largeImageText = text; + return this; + } + + public Builder setSmallImage(String key) { + return this.setSmallImage(key, ""); + } + + public Builder setSmallImage(@NotNull String key, String text) { + // Null check used for users blatantly ignoring the NotNull marker + if ((text != null && !text.isEmpty()) && key != null) { + throw new IllegalArgumentException("Image key cannot be null when assigning a hover text"); + } + + rpc.smallImageKey = key; + rpc.smallImageText = text; + return this; + } + + public Builder setParty(String party, int size, int max) { + // Buttons are present, ignore + if ((rpc.button_label_1 != null && rpc.button_label_1.isEmpty()) || (rpc.button_label_2 != null && rpc.button_label_2.isEmpty())) + return this; + + rpc.partyId = party; + rpc.partySize = size; + rpc.partyMax = max; + return this; + } + + public Builder setSecrets(String match, String join, String spectate) { + // Buttons are present, ignore + if ((rpc.button_label_1 != null && rpc.button_label_1.isEmpty()) || (rpc.button_label_2 != null && rpc.button_label_2.isEmpty())) + return this; + + rpc.matchSecret = match; + rpc.joinSecret = join; + rpc.spectateSecret = spectate; + return this; + } + + public Builder setSecrets(String join, String spectate) { + // Buttons are present, ignore + if ((rpc.button_label_1 != null && rpc.button_label_1.isEmpty()) || (rpc.button_label_2 != null && rpc.button_label_2.isEmpty())) + return this; + + rpc.joinSecret = join; + rpc.spectateSecret = spectate; + return this; + } + + public Builder setInstance(boolean i) { + // Buttons are present, ignore + if ((rpc.button_label_1 != null && rpc.button_label_1.isEmpty()) || (rpc.button_label_2 != null && rpc.button_label_2.isEmpty())) + return this; + + rpc.instance = i ? 1 : 0; + return this; + } + + public Builder setButtons(RPCButton button) { + return this.setButtons(Collections.singletonList(button)); + } + + public Builder setButtons(RPCButton button1, RPCButton button2) { + return this.setButtons(Arrays.asList(button1, button2)); + } + + public Builder setButtons(List rpcButtons) { + // Limit to 2 Buttons. Discord Limitation + if (rpcButtons != null && !rpcButtons.isEmpty()) { + int length = Math.min(rpcButtons.size(), 2); + rpc.button_label_1 = rpcButtons.get(0).getLabel(); + rpc.button_url_1 = rpcButtons.get(0).getUrl(); + + if (length == 2) { + rpc.button_label_2 = rpcButtons.get(1).getLabel(); + rpc.button_url_2 = rpcButtons.get(1).getUrl(); + } + } + + return this; + } + + public DiscordRichPresence build() { + return rpc; + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordUser.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordUser.java new file mode 100644 index 0000000..e8bc085 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/DiscordUser.java @@ -0,0 +1,39 @@ +package com.hypherionmc.craterlib.core.rpcsdk; + +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * @author HypherionSA + * Class representing the Discord User + */ +public class DiscordUser extends Structure { + + // The User ID of the User + public String userId; + + // The Username of the User + public String username; + + // The unique identifier of the user. Discontinued by Discord + @Deprecated + public String discriminator; + + // The avatar has of the user + public String avatar; + + /** + * DO NOT TOUCH THIS... EVER! + */ + @Override + protected List getFieldOrder() { + return Arrays.asList( + "userId", + "username", + "discriminator", + "avatar" + ); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/DisconnectedCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/DisconnectedCallback.java new file mode 100644 index 0000000..7ea59e1 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/DisconnectedCallback.java @@ -0,0 +1,18 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when the Discord RPC disconnects + */ +public interface DisconnectedCallback extends Callback { + + /** + * Called when RPC disconnected + * + * @param errorCode Error code if any + * @param message Details about the disconnection + */ + void apply(int errorCode, String message); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ErroredCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ErroredCallback.java new file mode 100644 index 0000000..1f86c90 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ErroredCallback.java @@ -0,0 +1,18 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when the RPC ran into an error + */ +public interface ErroredCallback extends Callback { + + /** + * Called when an RPC error occurs + * + * @param errorCode Error code if any + * @param message Details about the error + */ + void apply(int errorCode, String message); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinGameCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinGameCallback.java new file mode 100644 index 0000000..cc752af --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinGameCallback.java @@ -0,0 +1,17 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when someone was approved to join your game + */ +public interface JoinGameCallback extends Callback { + + /** + * Called when someone joins a game from {@link JoinRequestCallback} + * + * @param joinSecret Secret or Password required to let the player join the game + */ + void apply(String joinSecret); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinRequestCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinRequestCallback.java new file mode 100644 index 0000000..115fd4f --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/JoinRequestCallback.java @@ -0,0 +1,19 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.hypherionmc.craterlib.core.rpcsdk.DiscordUser; +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when someone requests to join your game + */ +public interface JoinRequestCallback extends Callback { + + /** + * Called when someone clicks on the Join Game button + * + * @param user The Discord User trying to join your game + * @see DiscordUser + */ + void apply(DiscordUser user); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ReadyCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ReadyCallback.java new file mode 100644 index 0000000..66f3b59 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/ReadyCallback.java @@ -0,0 +1,19 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.hypherionmc.craterlib.core.rpcsdk.DiscordUser; +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when the RPC has connected successfully + */ +public interface ReadyCallback extends Callback { + + /** + * Called when the RPC is connected and ready to be used + * + * @param user The user the RPC is displayed on + * @see DiscordUser + */ + void apply(DiscordUser user); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/SpectateGameCallback.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/SpectateGameCallback.java new file mode 100644 index 0000000..979e53d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/callbacks/SpectateGameCallback.java @@ -0,0 +1,17 @@ +package com.hypherionmc.craterlib.core.rpcsdk.callbacks; + +import com.sun.jna.Callback; + +/** + * @author HypherionSA + * Callback for when someone is requesting to spectate your game + */ +public interface SpectateGameCallback extends Callback { + + /** + * Called when joining the game + * + * @param spectateSecret Secret or Password required to let the player spectate + */ + void apply(String spectateSecret); +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/helpers/RPCButton.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/helpers/RPCButton.java new file mode 100644 index 0000000..ab2bdc8 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/core/rpcsdk/helpers/RPCButton.java @@ -0,0 +1,55 @@ +package com.hypherionmc.craterlib.core.rpcsdk.helpers; + +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; + +/** + * @author HypherionSA + * Helper class to add Buttons to Discord Rich Presence + * This can not be used with Join/Spectate + */ +public class RPCButton implements Serializable { + + // The label of the button + private final String label; + + // The URL the button will open when clicked + private final String url; + + protected RPCButton(String label, String url) { + this.label = label; + this.url = url; + } + + /** + * Create a new RPC Button + * + * @param label The label of the button + * @param url The URL the button will open when clicked + * @return The constructed button + */ + public static RPCButton create(@NotNull String label, @NotNull String url) { + // Null check used here for users blatantly ignoring the NotNull marker + if (label == null || label.isEmpty() || url == null || url.isEmpty()) { + throw new IllegalArgumentException("RPC Buttons require both a label and url"); + } + + label = label.substring(0, Math.min(label.length(), 31)); + return new RPCButton(label, url); + } + + /** + * @return The label assigned to the button + */ + public String getLabel() { + return label; + } + + /** + * @return The URL of the button + */ + public String getUrl() { + return url; + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/ChatInputSuggestorMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/ChatInputSuggestorMixin.java new file mode 100644 index 0000000..c48dfbc --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/ChatInputSuggestorMixin.java @@ -0,0 +1,84 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.client.mentions.MentionsController; +import net.minecraft.client.gui.components.CommandSuggestions; +import net.minecraft.client.gui.components.EditBox; +import org.objectweb.asm.Opcodes; +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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * @author HypherionSA + * Allow Users, Roles and Channels to be pingable from MC chat (Client Side) + */ +@Mixin(CommandSuggestions.class) +public abstract class ChatInputSuggestorMixin { + + @Shadow + public abstract void showSuggestions(boolean p_93931_); + + @Shadow @Final + EditBox input; + + @Shadow + private static int getLastWordIndex(String p_93913_) { + return 0; + } + + @Inject( + method = "updateCommandInfo", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/client/gui/components/CommandSuggestions;pendingSuggestions:Ljava/util/concurrent/CompletableFuture;", + opcode = Opcodes.PUTFIELD, + shift = At.Shift.AFTER, + ordinal = 0 + ), + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/components/CommandSuggestions;getLastWordIndex(Ljava/lang/String;)I" + ) + ) + ) + private void injectSuggestions(CallbackInfo ci) { + if (MentionsController.hasMentions() && MentionsController.isLastMentionConditional()) { + this.showSuggestions(true); + } + } + + @SuppressWarnings("InvalidInjectorMethodSignature") + @ModifyVariable(method = "updateCommandInfo", at = @At(value = "STORE"), ordinal = 0, name = "collection") + private Collection injectMentions(Collection vanilla) { + if (!MentionsController.hasMentions()) + return vanilla; + + ArrayList newSuggest = new ArrayList<>(vanilla); + + String currentInput = this.input.getValue(); + int currentCursorPosition = this.input.getCursorPosition(); + + String textBeforeCursor = currentInput.substring(0, currentCursorPosition); + int startOfCurrentWord = getLastWordIndex(textBeforeCursor); + + String currentWord = textBeforeCursor.substring(startOfCurrentWord); + String finalWord = currentWord.replace("[", "").replace("]", ""); + + Collection mentions = MentionsController.getMentions(finalWord); + + if (!mentions.isEmpty()) { + mentions.forEach(m -> newSuggest.add("[" + m + "]")); + } + + return newSuggest; + } +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/CommandMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/CommandMixin.java new file mode 100644 index 0000000..bb9dec4 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/CommandMixin.java @@ -0,0 +1,37 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.google.common.base.Throwables; +import com.hypherionmc.craterlib.api.events.server.CraterCommandEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.mojang.brigadier.ParseResults; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +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.CallbackInfo; + +@Mixin(Commands.class) +public class CommandMixin { + + @Inject(method = "performCommand", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/commands/Commands;finishParsing(Lcom/mojang/brigadier/ParseResults;Ljava/lang/String;Lnet/minecraft/commands/CommandSourceStack;)Lcom/mojang/brigadier/context/ContextChain;", + shift = At.Shift.BEFORE + ), cancellable = true + ) + private void injectCommandEvent(ParseResults stackParseResults, String command, CallbackInfo ci) { + CraterCommandEvent commandEvent = CraterCommandEvent.of(stackParseResults, command); + CraterEventBus.INSTANCE.postEvent(commandEvent); + if (commandEvent.wasCancelled()) { + ci.cancel(); + return; + } + + if (commandEvent.getException() != null) { + Throwables.throwIfUnchecked(commandEvent.getException()); + ci.cancel(); + } + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerAdvancementsMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerAdvancementsMixin.java new file mode 100644 index 0000000..30fa64f --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerAdvancementsMixin.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.CraterAdvancementEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.advancements.BridgedAdvancement; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.server.PlayerAdvancements; +import net.minecraft.server.level.ServerPlayer; +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.CallbackInfoReturnable; + +@Mixin(PlayerAdvancements.class) +public class PlayerAdvancementsMixin { + + @Shadow + private ServerPlayer player; + + @Inject(method = "award", at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/AdvancementRewards;grant(Lnet/minecraft/server/level/ServerPlayer;)V", shift = At.Shift.AFTER)) + private void injectAdvancementEvent(AdvancementHolder advancementHolder, String string, CallbackInfoReturnable cir) { + Advancement advancement = advancementHolder.value(); + + if (advancement.display().isPresent() && advancement.display().get().shouldAnnounceChat()) { + CraterEventBus.INSTANCE.postEvent(new CraterAdvancementEvent(BridgedPlayer.of(this.player), BridgedAdvancement.of(advancementHolder.value()))); + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerListMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerListMixin.java new file mode 100644 index 0000000..3bc6f6b --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerListMixin.java @@ -0,0 +1,53 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.server.CraterPlayerEvent; +import com.hypherionmc.craterlib.api.events.server.MessageBroadcastEvent; +import com.hypherionmc.craterlib.api.events.server.PlayerPreLoginEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import com.mojang.authlib.GameProfile; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.CommonListenerCookie; +import net.minecraft.server.players.PlayerList; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.net.SocketAddress; +import java.util.function.Function; + +@Mixin(PlayerList.class) +public class PlayerListMixin { + + @Inject(method = "broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Ljava/util/function/Function;Z)V", at = @At("HEAD")) + private void injectBroadcastEvent(Component component, Function function, boolean bl, CallbackInfo ci) { + String thread = Thread.currentThread().getStackTrace()[3].getClassName(); + MessageBroadcastEvent event = new MessageBroadcastEvent(ChatUtils.mojangToAdventure(component), (f) -> ChatUtils.mojangToAdventure(component), bl, thread); + CraterEventBus.INSTANCE.postEvent(event); + } + + @Inject(method = "placeNewPlayer", at = @At("TAIL")) + private void injectPlayerLoginEvent(Connection connection, ServerPlayer serverPlayer, CommonListenerCookie commonListenerCookie, CallbackInfo ci) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedIn(BridgedPlayer.of(serverPlayer))); + } + + @Inject(method = "remove", at = @At("HEAD")) + private void injectPlayerLogoutEvent(ServerPlayer player, CallbackInfo ci) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedOut(BridgedPlayer.of(player))); + } + + @Inject(method = "canPlayerLogin", at = @At("HEAD"), cancellable = true) + private void injectPreLoginEvent(SocketAddress address, GameProfile gameProfile, CallbackInfoReturnable cir) { + PlayerPreLoginEvent event = new PlayerPreLoginEvent(address, BridgedGameProfile.of(gameProfile)); + CraterEventBus.INSTANCE.postEvent(event); + if (event.getMessage() != null) { + cir.setReturnValue(ChatUtils.adventureToMojang(event.getMessage())); + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerMixin.java new file mode 100644 index 0000000..5fd03bd --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/PlayerMixin.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.common.CraterPlayerDeathEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Mixin(Player.class) +public class PlayerMixin { + + @Inject(method = "die", at = @At("HEAD")) + private void injectPlayerDeathEvent(DamageSource damageSource, CallbackInfo ci) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerDeathEvent(BridgedPlayer.of(((Player) (Object) this)), damageSource)); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerPlayerMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerPlayerMixin.java new file mode 100644 index 0000000..ae028e7 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerPlayerMixin.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.mixin.events; + +import com.hypherionmc.craterlib.api.events.common.CraterPlayerDeathEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +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.CallbackInfo; + +@Mixin(ServerPlayer.class) +public class ServerPlayerMixin { + + @Inject(method = "die", at = @At("HEAD")) + private void injectPlayerDeathEvent(DamageSource damageSource, CallbackInfo ci) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerDeathEvent(BridgedPlayer.of(((ServerPlayer) (Object) this)), damageSource)); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/ServerStatusMixin.java new file mode 100644 index 0000000..53985e7 --- /dev/null +++ b/1.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/ClientLevelMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/ClientLevelMixin.java new file mode 100644 index 0000000..2368d6d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/ClientLevelMixin.java @@ -0,0 +1,25 @@ +package com.hypherionmc.craterlib.mixin.events.client; + +import com.hypherionmc.craterlib.api.events.client.CraterSinglePlayerEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Mixin(ClientLevel.class) +public class ClientLevelMixin { + + @Inject(method = "addEntity", at = @At("HEAD")) + private void injectSinglePlayerJoinEvent(Entity entity, CallbackInfo ci) { + if (entity instanceof Player player) { + CraterSinglePlayerEvent.PlayerLogin playerLogin = new CraterSinglePlayerEvent.PlayerLogin(BridgedPlayer.of(player)); + CraterEventBus.INSTANCE.postEvent(playerLogin); + } + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/MinecraftMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/MinecraftMixin.java new file mode 100644 index 0000000..2903131 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/MinecraftMixin.java @@ -0,0 +1,31 @@ +package com.hypherionmc.craterlib.mixin.events.client; + +import com.hypherionmc.craterlib.api.events.client.ScreenEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.client.gui.BridgedScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; +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(Minecraft.class) +public class MinecraftMixin { + + @Shadow + @Nullable + public Screen screen; + + @Inject(method = "setScreen", at = @At(value = "TAIL")) + private void injectScreenOpeningEvent(Screen screen, CallbackInfo ci) { + Screen old = this.screen; + if (screen != null) { + ScreenEvent.Opening opening = new ScreenEvent.Opening(BridgedScreen.of(old), BridgedScreen.of(screen)); + CraterEventBus.INSTANCE.postEvent(opening); + } + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/RealmsMainScreenMixin.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/RealmsMainScreenMixin.java new file mode 100644 index 0000000..c1ce895 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/mixin/events/client/RealmsMainScreenMixin.java @@ -0,0 +1,23 @@ +package com.hypherionmc.craterlib.mixin.events.client; + +import com.hypherionmc.craterlib.api.events.client.PlayerJoinRealmEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.realmsclient.dto.BridgedRealmsServer; +import com.mojang.realmsclient.RealmsMainScreen; +import com.mojang.realmsclient.dto.RealmsServer; +import net.minecraft.client.gui.screens.Screen; +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.CallbackInfo; + +@Mixin(RealmsMainScreen.class) +public class RealmsMainScreenMixin { + + @Inject(at = @At("HEAD"), method = "play(Lcom/mojang/realmsclient/dto/RealmsServer;Lnet/minecraft/client/gui/screens/Screen;Z)V") + private static void play(RealmsServer serverData, Screen arg2, boolean bl, CallbackInfo ci) { + PlayerJoinRealmEvent playerJoinRealm = new PlayerJoinRealmEvent(BridgedRealmsServer.of(serverData)); + CraterEventBus.INSTANCE.postEvent(playerJoinRealm); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedAdvancement.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedAdvancement.java new file mode 100644 index 0000000..19a7e7a --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedAdvancement.java @@ -0,0 +1,21 @@ +package com.hypherionmc.craterlib.nojang.advancements; + +import lombok.RequiredArgsConstructor; +import net.minecraft.advancements.Advancement; + +import java.util.Optional; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedAdvancement { + + private final Advancement internal; + + public Optional displayInfo() { + if (internal.display().isPresent()) { + return Optional.of(BridgedDisplayInfo.of(internal.display().get())); + } + + return Optional.empty(); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedDisplayInfo.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedDisplayInfo.java new file mode 100644 index 0000000..5a0c155 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/advancements/BridgedDisplayInfo.java @@ -0,0 +1,29 @@ +package com.hypherionmc.craterlib.nojang.advancements; + +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.advancements.DisplayInfo; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedDisplayInfo { + + private final DisplayInfo internal; + + public boolean shouldDisplay() { + return internal.shouldAnnounceChat(); + } + + public boolean isHidden() { + return internal.isHidden(); + } + + public Component displayName() { + return ChatUtils.mojangToAdventure(internal.getTitle()); + } + + public Component description() { + return ChatUtils.mojangToAdventure(internal.getDescription()); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/authlib/BridgedGameProfile.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/authlib/BridgedGameProfile.java new file mode 100644 index 0000000..c83f3c4 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/authlib/BridgedGameProfile.java @@ -0,0 +1,29 @@ +package com.hypherionmc.craterlib.nojang.authlib; + +import com.mojang.authlib.GameProfile; +import lombok.RequiredArgsConstructor; + +import java.util.UUID; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedGameProfile { + + private final GameProfile internal; + + public static BridgedGameProfile mojang(UUID id, String name) { + return new BridgedGameProfile(new GameProfile(id, name)); + } + + public String getName() { + return internal.getName(); + } + + public UUID getId() { + return internal.getId(); + } + + public GameProfile toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedMinecraft.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedMinecraft.java new file mode 100644 index 0000000..57db3f9 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedMinecraft.java @@ -0,0 +1,87 @@ +package com.hypherionmc.craterlib.nojang.client; + +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedServerData; +import com.hypherionmc.craterlib.nojang.client.server.BridgedIntegratedServer; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import lombok.Getter; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.UUID; + +public class BridgedMinecraft { + + @Getter + private static final BridgedMinecraft instance = new BridgedMinecraft(); + private final Minecraft internal = Minecraft.getInstance(); + + public File getGameDirectory() { + return internal.gameDirectory; + } + + public BridgedOptions getOptions() { + return BridgedOptions.of(internal.options); + } + + @Nullable + public BridgedClientLevel getLevel() { + if (internal.level == null) + return null; + + return BridgedClientLevel.of(internal.level); + } + + public boolean isRealmServer() { + return internal.getCurrentServer() != null && internal.getCurrentServer().isRealm(); + } + + public boolean isSinglePlayer() { + return internal.hasSingleplayerServer(); + } + + @Nullable + public BridgedPlayer getPlayer() { + if (internal.player == null) + return null; + + return BridgedPlayer.of(internal.player); + } + + public String getGameVersion() { + return SharedConstants.getCurrentVersion().getName(); + } + + public String getUserName() { + return internal.getUser().getName(); + } + + public UUID getPlayerId() { + return internal.getUser().getProfileId(); + } + + @Nullable + public BridgedServerData getCurrentServer() { + if (internal.getCurrentServer() == null) + return null; + + return BridgedServerData.of(internal.getCurrentServer()); + } + + @Nullable + public BridgedIntegratedServer getSinglePlayerServer() { + if (internal.getSingleplayerServer() == null) + return null; + + return BridgedIntegratedServer.of(internal.getSingleplayerServer()); + } + + public int getServerPlayerCount () { + if (internal.getConnection() == null) + return 0; + + return internal.getConnection().getOnlinePlayers().size(); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedOptions.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedOptions.java new file mode 100644 index 0000000..7063feb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/BridgedOptions.java @@ -0,0 +1,15 @@ +package com.hypherionmc.craterlib.nojang.client; + +import lombok.RequiredArgsConstructor; +import net.minecraft.client.Options; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedOptions { + + private final Options internal; + + public String getLanguage() { + return internal.languageCode; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java new file mode 100644 index 0000000..c4bcc23 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/gui/BridgedScreen.java @@ -0,0 +1,36 @@ +package com.hypherionmc.craterlib.nojang.client.gui; + +import lombok.RequiredArgsConstructor; +import net.minecraft.client.gui.screens.LevelLoadingScreen; +import net.minecraft.client.gui.screens.ReceivingLevelScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; +import net.minecraft.realms.RealmsScreen; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedScreen { + + private final Screen internal; + + public boolean isTitleScreen() { + return internal instanceof TitleScreen; + } + + public boolean isRealmsScreen() { + return internal instanceof RealmsScreen; + } + + public boolean isServerBrowserScreen() { + return internal instanceof JoinMultiplayerScreen; + } + + public boolean isLoadingScreen() { + return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen; + } + + public Screen toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedClientLevel.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedClientLevel.java new file mode 100644 index 0000000..4e7b60b --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedClientLevel.java @@ -0,0 +1,59 @@ +package com.hypherionmc.craterlib.nojang.client.multiplayer; + +import com.hypherionmc.craterlib.nojang.core.BridgedBlockPos; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.client.multiplayer.ClientLevel; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.atomic.AtomicReference; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedClientLevel { + + private final ClientLevel internal; + + public boolean isClientSide() { + return internal.isClientSide(); + } + + public long getGameTime() { + return internal.getGameTime(); + } + + public long getDayTime() { + return internal.getDayTime(); + } + + public long dayTime() { + return internal.dayTime(); + } + + public boolean isRaining() { + return internal.isRaining(); + } + + public boolean isThundering() { + return internal.isThundering(); + } + + @Nullable + public ResourceIdentifier getDimensionKey() { + return ResourceIdentifier.fromMojang(internal.dimension().location()); + } + + @Nullable + public ResourceIdentifier getBiomeIdentifier(BridgedBlockPos onPos) { + AtomicReference identifier = new AtomicReference<>(null); + internal.getBiome(onPos.toMojang()).unwrap().ifLeft(b -> identifier.set(ResourceIdentifier.fromMojang(b.location()))); + return identifier.get(); + } + + @Nullable + public Component getDifficulty() { + return ChatUtils.mojangToAdventure(internal.getDifficulty().getDisplayName()); + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedServerData.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedServerData.java new file mode 100644 index 0000000..ea56245 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/multiplayer/BridgedServerData.java @@ -0,0 +1,37 @@ +package com.hypherionmc.craterlib.nojang.client.multiplayer; + +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.client.multiplayer.ServerData; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedServerData { + + private final ServerData internal; + + public String name() { + return internal.name; + } + + public String ip() { + return internal.ip; + } + + public Component motd() { + return ChatUtils.mojangToAdventure(internal.motd); + } + + public int getMaxPlayers() { + if (internal.players == null) { + return internal.playerList.size() + 1; + } + + return internal.players.max(); + } + + public ServerData toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/server/BridgedIntegratedServer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/server/BridgedIntegratedServer.java new file mode 100644 index 0000000..0ecacaf --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/client/server/BridgedIntegratedServer.java @@ -0,0 +1,19 @@ +package com.hypherionmc.craterlib.nojang.client.server; + +import lombok.RequiredArgsConstructor; +import net.minecraft.client.server.IntegratedServer; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedIntegratedServer { + + private final IntegratedServer internal; + + public String getLevelName() { + return internal.getWorldData().getLevelName(); + } + + public IntegratedServer toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java new file mode 100644 index 0000000..5529824 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedCommandSourceStack.java @@ -0,0 +1,26 @@ +package com.hypherionmc.craterlib.nojang.commands; + +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.commands.CommandSourceStack; + +import java.util.function.Supplier; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedCommandSourceStack { + + private final CommandSourceStack internal; + + public void sendSuccess(Supplier supplier, boolean bl) { + 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.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedFakePlayer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedFakePlayer.java new file mode 100644 index 0000000..42b65cb --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/commands/BridgedFakePlayer.java @@ -0,0 +1,55 @@ +package com.hypherionmc.craterlib.nojang.commands; + +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public abstract class BridgedFakePlayer { + + final MojangBridge internal; + + public BridgedFakePlayer(BridgedMinecraftServer server, int perm, String name) { + internal = new MojangBridge(server.toMojang(), perm, name, this::onSuccess, this::onError); + } + + public abstract void onSuccess(Supplier supplier, Boolean aBoolean); + + public void onError(net.kyori.adventure.text.Component component) { + this.onSuccess(() -> component, false); + } + + public CommandSourceStack toMojang() { + return internal; + } + + static class MojangBridge extends CommandSourceStack { + + private final BiConsumer, Boolean> successCallback; + public final Consumer errorCallback; + + MojangBridge(MinecraftServer server, int perm, String name, BiConsumer, Boolean> successCallback, Consumer errorCallback) { + super(CommandSource.NULL, Vec3.ZERO, Vec2.ZERO, server.overworld(), perm, name, Component.literal(name), server, null); + this.successCallback = successCallback; + this.errorCallback = errorCallback; + } + + @Override + public void sendSuccess(Supplier supplier, boolean bl) { + successCallback.accept(() -> ChatUtils.mojangToAdventure(supplier.get()), bl); + } + + @Override + public void sendFailure(Component arg) { + errorCallback.accept(ChatUtils.mojangToAdventure(arg)); + } + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/core/BridgedBlockPos.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/core/BridgedBlockPos.java new file mode 100644 index 0000000..bd49e2c --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/core/BridgedBlockPos.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.nojang.core; + +import lombok.RequiredArgsConstructor; +import net.minecraft.core.BlockPos; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedBlockPos { + + private final BlockPos internal; + + public int getX() { + return internal.getX(); + } + + public int getY() { + return internal.getY(); + } + + public int getZ() { + return internal.getZ(); + } + + public BlockPos toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/nbt/BridgedCompoundTag.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/nbt/BridgedCompoundTag.java new file mode 100644 index 0000000..17a2d04 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/nbt/BridgedCompoundTag.java @@ -0,0 +1,49 @@ +package com.hypherionmc.craterlib.nojang.nbt; + +import lombok.RequiredArgsConstructor; +import net.minecraft.nbt.CompoundTag; + +import java.util.Set; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedCompoundTag { + + private final CompoundTag internal; + + public static BridgedCompoundTag empty() { + return new BridgedCompoundTag(new CompoundTag()); + } + + public BridgedCompoundTag getCompound(String key) { + return BridgedCompoundTag.of(internal.getCompound(key)); + } + + public Set getAllKeys() { + return internal.getAllKeys(); + } + + public String getString(String key) { + return internal.getString(key); + } + + public boolean getBoolean(String key) { + return internal.getBoolean(key); + } + + public void putString(String key, String value) { + internal.putString(key, value); + } + + public void put(String key, BridgedCompoundTag value) { + internal.put(key, value.toMojang()); + } + + public void putBoolean(String key, boolean value) { + internal.putBoolean(key, value); + } + + public CompoundTag toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/BridgedFriendlyByteBuf.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/BridgedFriendlyByteBuf.java new file mode 100644 index 0000000..a7bafd0 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/BridgedFriendlyByteBuf.java @@ -0,0 +1,34 @@ +package com.hypherionmc.craterlib.nojang.network; + +import com.hypherionmc.craterlib.nojang.nbt.BridgedCompoundTag; +import lombok.RequiredArgsConstructor; +import net.minecraft.network.FriendlyByteBuf; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedFriendlyByteBuf { + + private final FriendlyByteBuf internal; + + public BridgedCompoundTag readNbt() { + return BridgedCompoundTag.of(internal.readNbt()); + } + + public BridgedFriendlyByteBuf writeNbt(BridgedCompoundTag tag) { + internal.writeNbt(tag.toMojang()); + return BridgedFriendlyByteBuf.of(internal); + } + + public BridgedFriendlyByteBuf writeUtf(String value) { + internal.writeUtf(value); + return BridgedFriendlyByteBuf.of(internal); + } + + public String readUtf() { + return internal.readUtf(); + } + + public FriendlyByteBuf toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/network/protocol/status/WrappedServerStatus.java b/1.21.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.21.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.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/package-info.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/package-info.java new file mode 100644 index 0000000..4c177a5 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/package-info.java @@ -0,0 +1,9 @@ +/** + * @author HypherionSA + * This package, called NoJang, exposes various wrapped API's. + * Using this api, a mod can essentially run on ANY minecraft version this library + * supports, from one code base. + * IMPORTANT NOTE: THESE API'S MUST NEVER EXPOSE ANY MINECRAFT CLASSES OR CODE!!!! + * THEY MUST ALWAYS BE HANDLED INTERNALLY AND ONLY RETURN WRAPPED VARIANTS + */ +package com.hypherionmc.craterlib.nojang; \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/realmsclient/dto/BridgedRealmsServer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/realmsclient/dto/BridgedRealmsServer.java new file mode 100644 index 0000000..dddf746 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/realmsclient/dto/BridgedRealmsServer.java @@ -0,0 +1,40 @@ +package com.hypherionmc.craterlib.nojang.realmsclient.dto; + +import com.mojang.realmsclient.dto.PlayerInfo; +import com.mojang.realmsclient.dto.RealmsServer; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedRealmsServer { + + private final RealmsServer internal; + + public String getName() { + return internal.getName(); + } + + public String getDescription() { + return internal.getDescription(); + } + + public String getWorldType() { + return internal.worldType.name(); + } + + public String getMinigameName() { + return internal.getMinigameName(); + } + + public String getMinigameImage() { + return internal.minigameImage; + } + + public long getPlayerCount() { + return internal.players.stream().filter(PlayerInfo::getOnline).count(); + } + + public RealmsServer toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/resources/ResourceIdentifier.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/resources/ResourceIdentifier.java new file mode 100644 index 0000000..379839d --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/resources/ResourceIdentifier.java @@ -0,0 +1,36 @@ +package com.hypherionmc.craterlib.nojang.resources; + +import net.minecraft.resources.ResourceLocation; + +public class ResourceIdentifier { + + private final ResourceLocation internal; + + public ResourceIdentifier(String namespace, String path) { + this.internal = ResourceLocation.fromNamespaceAndPath(namespace, path); + } + + public ResourceIdentifier(String path) { + this.internal = ResourceLocation.parse(path); + } + + public String getNamespace() { + return internal.getNamespace(); + } + + public String getPath() { + return internal.getPath(); + } + + public String getString() { + return internal.toString(); + } + + public static ResourceIdentifier fromMojang(ResourceLocation location) { + return new ResourceIdentifier(location.getNamespace(), location.getPath()); + } + + public ResourceLocation toMojang() { + return internal; + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/server/BridgedMinecraftServer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/server/BridgedMinecraftServer.java new file mode 100644 index 0000000..cb818b7 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/server/BridgedMinecraftServer.java @@ -0,0 +1,91 @@ +package com.hypherionmc.craterlib.nojang.server; + +import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import com.hypherionmc.craterlib.nojang.commands.BridgedFakePlayer; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.SharedConstants; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.players.UserBanListEntry; +import net.minecraft.server.players.UserWhiteListEntry; + +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedMinecraftServer { + + private final MinecraftServer internal; + + public boolean isUsingWhitelist() { + return internal.getPlayerList().isUsingWhitelist(); + } + + public int getPlayerCount() { + return internal.getPlayerList().getPlayerCount(); + } + + public int getMaxPlayers() { + return internal.getPlayerList().getMaxPlayers(); + } + + public String getServerModName() { + return internal.getServerModName(); + } + + public String getName() { + return SharedConstants.getCurrentVersion().getName(); + } + + public boolean usesAuthentication() { + return internal.usesAuthentication(); + } + + public void broadcastSystemMessage(Component text, boolean bl) { + internal.getPlayerList().broadcastSystemMessage(ChatUtils.adventureToMojang(text), bl); + } + + public boolean isPlayerBanned(BridgedGameProfile profile) { + return internal.getPlayerList().getBans().isBanned(profile.toMojang()); + } + + public void whitelistPlayer(BridgedGameProfile gameProfile) { + if (!internal.getPlayerList().isUsingWhitelist()) + return; + + internal.getPlayerList().getWhiteList().add(new UserWhiteListEntry(gameProfile.toMojang())); + } + + public void unWhitelistPlayer(BridgedGameProfile gameProfile) { + if (!internal.getPlayerList().isUsingWhitelist()) + return; + + internal.getPlayerList().getWhiteList().remove(new UserWhiteListEntry(gameProfile.toMojang())); + } + + public List getPlayers() { + List profiles = new ArrayList<>(); + + if (internal.getPlayerList() == null) + return profiles; + + internal.getPlayerList().getPlayers().forEach(p -> profiles.add(BridgedPlayer.of(p))); + + return profiles; + } + + public void banPlayer(BridgedGameProfile profile) { + internal.getPlayerList().getBans().add(new UserBanListEntry(profile.toMojang())); + } + + public void executeCommand(BridgedMinecraftServer server, BridgedFakePlayer player, String command) { + internal.getCommands().performPrefixedCommand(player.toMojang(), command); + } + + public MinecraftServer toMojang() { + return internal; + } + +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java new file mode 100644 index 0000000..2241836 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/nojang/world/entity/player/BridgedPlayer.java @@ -0,0 +1,68 @@ +package com.hypherionmc.craterlib.nojang.world.entity.player; + +import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; +import com.hypherionmc.craterlib.nojang.core.BridgedBlockPos; +import com.hypherionmc.craterlib.utils.ChatUtils; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +@RequiredArgsConstructor(staticName = "of") +public class BridgedPlayer { + + private final Player internal; + + public Component getDisplayName() { + return ChatUtils.mojangToAdventure(internal.getDisplayName()); + } + + public Component getName() { + return ChatUtils.mojangToAdventure(internal.getName()); + } + + public UUID getUUID() { + return internal.getUUID(); + } + + public String getStringUUID() { + return internal.getStringUUID(); + } + + public BridgedGameProfile getGameProfile() { + return BridgedGameProfile.of(internal.getGameProfile()); + } + + public boolean isServerPlayer() { + return internal instanceof ServerPlayer; + } + + public Player toMojang() { + return internal; + } + + public BridgedBlockPos getOnPos() { + return BridgedBlockPos.of(internal.getOnPos()); + } + + @Nullable + public ServerGamePacketListenerImpl getConnection() { + if (isServerPlayer()) { + return ((ServerPlayer) internal).connection; + } + 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.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java new file mode 100644 index 0000000..a68e6f7 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/ChatUtils.java @@ -0,0 +1,122 @@ +package com.hypherionmc.craterlib.utils; + +import com.hypherionmc.craterlib.core.platform.CommonPlatform; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; +import me.hypherionmc.mcdiscordformatter.discord.DiscordSerializer; +import me.hypherionmc.mcdiscordformatter.minecraft.MinecraftSerializer; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.json.JSONOptions; +import net.minecraft.ChatFormatting; +import net.minecraft.SharedConstants; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; + +public class ChatUtils { + + private static final GsonComponentSerializer adventureSerializer = GsonComponentSerializer.builder().options( + JSONOptions.byDataVersion().at(SharedConstants.getCurrentVersion().getDataVersion().getVersion()) + ).build(); + + private static final MiniMessage miniMessage = MiniMessage.miniMessage(); + + public static Component adventureToMojang(net.kyori.adventure.text.Component inComponent) { + final String serialised = adventureSerializer.serialize(inComponent); + return Component.Serializer.fromJson(serialised, getRegistryLookup()); + } + + public static net.kyori.adventure.text.Component mojangToAdventure(Component inComponent) { + final String serialised = Component.Serializer.toJson(inComponent, getRegistryLookup()); + return adventureSerializer.deserialize(serialised); + } + + private static HolderLookup.Provider getRegistryLookup() { + if (ModloaderEnvironment.INSTANCE.getEnvironment().isClient() && Minecraft.getInstance().level != null) + return Minecraft.getInstance().level.registryAccess(); + + if (ModloaderEnvironment.INSTANCE.getEnvironment().isServer() && CommonPlatform.INSTANCE.getMCServer() != null) + return CommonPlatform.INSTANCE.getMCServer().toMojang().registryAccess(); + + return RegistryAccess.EMPTY; + } + + // Some text components contain duplicate text, resulting in duplicate messages + // sent back to discord. This should help fix those issues + public static Component safeCopy(Component inComponent) { + String value = inComponent.getString(); + Style style = inComponent.getStyle(); + return Component.literal(value).withStyle(style); + } + + public static String strip(String inString, String... toStrip) { + String finalString = inString; + + for (String strip : toStrip) { + if (finalString.startsWith(strip)) + finalString = finalString.replaceFirst(strip, ""); + + if (finalString.startsWith(" ")) + finalString = finalString.replaceFirst(" ", ""); + } + + return finalString; + } + + public static String resolve(net.kyori.adventure.text.Component component, boolean formatted) { + Component c = adventureToMojang(component); + String returnVal = ChatFormatting.stripFormatting(DiscordMarkdownStripper.stripMarkdown(c.getString())); + + if (formatted) { + returnVal = DiscordSerializer.INSTANCE.serialize(safeCopy(c).copy()); + } + + return returnVal; + } + + public static net.kyori.adventure.text.Component resolve(String component, boolean formatted) { + Component returnVal = Component.literal(component); + if (formatted) { + returnVal = MinecraftSerializer.INSTANCE.serialize(component); + } + + return mojangToAdventure(returnVal); + } + + public static net.kyori.adventure.text.Component getTooltipTitle(String key) { + return net.kyori.adventure.text.Component.text(NamedTextColor.YELLOW + net.kyori.adventure.text.Component.translatable(key).key()); + } + + public static String resolveTranslation(String key) { + return net.kyori.adventure.text.Component.translatable(key).key(); + } + + public static net.kyori.adventure.text.Component getTranslation(String key) { + return net.kyori.adventure.text.Component.translatable(key); + } + + public static net.kyori.adventure.text.Component makeComponent(String text) { + return net.kyori.adventure.text.Component.translatable(text); + } + + public static net.kyori.adventure.text.Component getBiomeName(ResourceIdentifier identifier) { + if (identifier == null) + return net.kyori.adventure.text.Component.text("Unknown"); + + return mojangToAdventure(Component.translatable(Util.makeDescriptionId("biome", identifier.toMojang()))); + } + + public static net.kyori.adventure.text.Component format(String value) { + value = convertFormattingCodes(value); + return miniMessage.deserializeOr(value, net.kyori.adventure.text.Component.translatable(value)); + } + + private static String convertFormattingCodes(String input) { + return input.replaceAll("§([0-9a-fklmnor])", "\u00A7$1"); + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/DiscordMarkdownStripper.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/DiscordMarkdownStripper.java new file mode 100644 index 0000000..0175b4f --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/DiscordMarkdownStripper.java @@ -0,0 +1,35 @@ +package com.hypherionmc.craterlib.utils; + +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +public class DiscordMarkdownStripper { + + // Patterns for different markdown syntaxes + private static final Pattern BOLD = Pattern.compile("\\*\\*(.*?)\\*\\*"); + private static final Pattern ITALIC_UNDERSCORE = Pattern.compile("_(.*?)_"); + private static final Pattern ITALIC_ASTERISK = Pattern.compile("\\*(.*?)\\*"); + private static final Pattern UNDERLINE = Pattern.compile("__(.*?)__"); + private static final Pattern STRIKETHROUGH = Pattern.compile("~~(.*?)~~"); + private static final Pattern CODE_BLOCK = Pattern.compile("```(.+?)```", Pattern.DOTALL); + private static final Pattern INLINE_CODE = Pattern.compile("`([^`]*)`"); + private static final Pattern BLOCKQUOTE = Pattern.compile("^> (.*?$)", Pattern.MULTILINE); + private static final Pattern MARKDOWN_LINK = Pattern.compile("\\[(.*?)\\]\\((.*?)\\)"); + private static final Pattern PLAIN_URL = Pattern.compile("\\b(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); + + public static String stripMarkdown(@NotNull String text) { + text = BOLD.matcher(text).replaceAll("$1"); + text = ITALIC_UNDERSCORE.matcher(text).replaceAll("$1"); + text = ITALIC_ASTERISK.matcher(text).replaceAll("$1"); + text = UNDERLINE.matcher(text).replaceAll("$1"); + text = STRIKETHROUGH.matcher(text).replaceAll("$1"); + text = CODE_BLOCK.matcher(text).replaceAll("$1"); + text = INLINE_CODE.matcher(text).replaceAll("$1"); + text = BLOCKQUOTE.matcher(text).replaceAll("$1"); + text = MARKDOWN_LINK.matcher(text).replaceAll("$1"); + text = PLAIN_URL.matcher(text).replaceAll("<$0>"); + + return text; + } +} diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/InternalServiceUtil.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/InternalServiceUtil.java new file mode 100644 index 0000000..2305d14 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/InternalServiceUtil.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.utils; + +import com.hypherionmc.craterlib.CraterConstants; + +import java.util.ServiceLoader; + +/** + * @author HypherionSA + * Utility class to handle SPI loading + */ +public class InternalServiceUtil { + + /** + * Try to load a service + * + * @param clazz The service class type to load + * @return The loaded class + */ + public static T load(Class clazz) { + final T loadedService = ServiceLoader.load(clazz) + .findFirst() + .orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName())); + CraterConstants.LOG.debug("Loaded {} for service {}", loadedService, clazz); + return loadedService; + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/OptifineUtils.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/OptifineUtils.java new file mode 100644 index 0000000..8a06da2 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/OptifineUtils.java @@ -0,0 +1,46 @@ +package com.hypherionmc.craterlib.utils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author HypherionSA + * Utility class for Optifine compatibility + */ +public class OptifineUtils { + + private static final boolean hasOptifine = checkOptifine(); + + private static boolean checkOptifine() { + try { + Class ofConfigClass = Class.forName("net.optifine.Config"); + return true; + } catch (ClassNotFoundException e) { + // Optifine is probably not present. Ignore the error + return false; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static boolean isRenderRegions() { + try { + Class ofConfigClass = Class.forName("net.optifine.Config"); + Method rrField = ofConfigClass.getMethod("isRenderRegions"); + return (boolean) rrField.invoke(null); + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | + IllegalAccessException e) { + // Optifine is probably not present. Ignore the error + return false; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static boolean hasOptifine() { + return hasOptifine; + } + +} \ No newline at end of file diff --git a/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/TriConsumer.java b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/TriConsumer.java new file mode 100644 index 0000000..77f7309 --- /dev/null +++ b/1.21.2/Common/src/main/java/com/hypherionmc/craterlib/utils/TriConsumer.java @@ -0,0 +1,5 @@ +package com.hypherionmc.craterlib.utils; + +public interface TriConsumer { + void accept(T t, U u, V v); +} diff --git a/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json b/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json new file mode 100644 index 0000000..03115d9 --- /dev/null +++ b/1.21.2/Common/src/main/resources/assets/craterlib/lang/en_us.json @@ -0,0 +1,8 @@ +{ + "t.clc.opensubconfig": "Open Config", + "t.clc.save": "Save", + "t.clc.cancel_discard": "Discard", + "t.clc.quit_config": "Unsaved Changes", + "t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?", + "t.clc.quit_discard": "Quit & Discard" +} diff --git a/1.21.2/Common/src/main/resources/craterlib.mixins.json b/1.21.2/Common/src/main/resources/craterlib.mixins.json new file mode 100644 index 0000000..c910ed2 --- /dev/null +++ b/1.21.2/Common/src/main/resources/craterlib.mixins.json @@ -0,0 +1,25 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.hypherionmc.craterlib.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "ChatInputSuggestorMixin", + "events.PlayerMixin", + "events.client.ClientLevelMixin", + "events.client.MinecraftMixin", + "events.client.RealmsMainScreenMixin" + ], + "server": [ + "events.CommandMixin", + "events.PlayerAdvancementsMixin", + "events.PlayerListMixin", + "events.ServerPlayerMixin", + "events.ServerStatusMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.2/Common/src/main/resources/pack.mcmeta b/1.21.2/Common/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..263d366 --- /dev/null +++ b/1.21.2/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "${mod_name}", + "pack_format": 18 + } +} diff --git a/1.21.2/Fabric/build.gradle b/1.21.2/Fabric/build.gradle new file mode 100644 index 0000000..a3a6220 --- /dev/null +++ b/1.21.2/Fabric/build.gradle @@ -0,0 +1,132 @@ +archivesBaseName = "${mod_name.replace(" ", "")}-Fabric-${minecraft_version}" + +dependencies { + // Core + modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api}" + + // Compat + modImplementation("com.terraformersmc:modmenu:${mod_menu_version}") { + exclude(group: "net.fabricmc.fabric-api") + } + + stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}") + + modImplementation "maven.modrinth:fabrictailor:${fabrictailor}" + modImplementation "maven.modrinth:vanish:${vanish}" + + // Do not edit or remove + implementation project(":Common") +} + +shadowJar { + from sourceSets.main.output + configurations = [project.configurations.shade] + + dependencies { + exclude(dependency('com.google.code.gson:.*')) + + relocate 'me.hypherionmc.moonconfig', 'shadow.hypherionmc.moonconfig' + relocate 'me.hypherionmc.mcdiscordformatter', 'shadow.hypherionmc.mcdiscordformatter' + relocate 'net.kyori', 'shadow.kyori' + } + + setArchiveClassifier('dev-shadow') + mergeServiceFiles() +} + +/** + * =============================================================================== + * = DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING = + * =============================================================================== + */ + +unimined.minecraft { + fabric { + loader fabric_loader + } +} + +remapJar { + inputFile.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier.set null +} + +jar { + archiveClassifier.set "dev" +} + +processResources { + from project(":Common").sourceSets.main.resources + def buildProps = project.properties.clone() + + filesMatching(['fabric.mod.json']) { + expand buildProps + } +} + +compileTestJava.enabled = false + +tasks.withType(JavaCompile).configureEach { + source(project(":Common").sourceSets.main.allSource) +} + +/** + * Publishing Config + */ +publishing { + publications { + mavenJava(MavenPublication) { + artifactId project.archivesBaseName + from components.java + + artifact(remapJar) { + builtBy remapJar + } + + pom.withXml { + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + it.artifactId.text() == 'regutils-joined-fabric' || + it.artifactId.text() == 'core' || + it.artifactId.text() == 'toml' + }.each() { + it.parent().remove(it) + } + } + } + } + + repositories { + maven rootProject.orion.getPublishingMaven() + } +} + +publisher { + apiKeys { + modrinth(System.getenv("MODRINTH_TOKEN")) + curseforge(System.getenv("CURSE_TOKEN")) + nightbloom(System.getenv("PLATFORM_KEY")) + } + + setCurseID(curse_id) + setModrinthID(modrinth_id) + setNightbloomID("craterlib") + setVersionType("release") + setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-fabric.md") + setProjectVersion("${minecraft_version}-${project.version}") + setDisplayName("[FABRIC/QUILT 1.21.2] CraterLib - ${project.version}") + setGameVersions("1.21.2") + setLoaders("fabric", "quilt") + setArtifact(remapJar) + setCurseEnvironment("both") + setIsManualRelease(true) + + modrinthDepends { + required("fabric-api") + } + + curseDepends { + required("fabric-api") + } +} \ No newline at end of file diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java new file mode 100644 index 0000000..5e93a3a --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibInitializer.java @@ -0,0 +1,40 @@ +package com.hypherionmc.craterlib; + +import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; +import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; +import com.hypherionmc.craterlib.common.FabricCommonPlatform; +import com.hypherionmc.craterlib.compat.Vanish; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +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.server.BridgedMinecraftServer; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; + +public class CraterLibInitializer implements ModInitializer { + + @Override + public void onInitialize() { + new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.SERVER)); + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(dispatcher)); + }); + + + ServerLifecycleEvents.SERVER_STARTING.register(server -> { + FabricCommonPlatform.server = server; + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Starting(BridgedMinecraftServer.of(server))); + }); + + ServerLifecycleEvents.SERVER_STARTED.register(li -> CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Started(BridgedMinecraftServer.of(li)))); + ServerLifecycleEvents.SERVER_STOPPING.register(server -> CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopping(BridgedMinecraftServer.of(server)))); + ServerLifecycleEvents.SERVER_STOPPED.register(server -> CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopped(BridgedMinecraftServer.of(server)))); + + if (ModloaderEnvironment.INSTANCE.isModLoaded("melius-vanish")) { + Vanish.register(); + } + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java new file mode 100644 index 0000000..5b34ff1 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/CraterLibModMenuIntegration.java @@ -0,0 +1,29 @@ +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.annotations.NoConfigScreen; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author HypherionSA + */ +public class CraterLibModMenuIntegration implements ModMenuApi { + + @Override + public Map> getProvidedConfigScreenFactories() { + Map> configScreens = new HashMap<>(); + + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { + if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { + configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen)); + } + }); + + return configScreens; + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/CraterLibClientInitializer.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/CraterLibClientInitializer.java new file mode 100644 index 0000000..b5ec5b1 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/CraterLibClientInitializer.java @@ -0,0 +1,27 @@ +package com.hypherionmc.craterlib.client; + +import com.hypherionmc.craterlib.api.events.client.CraterClientTickEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.network.CraterFabricNetworkHandler; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; + +public class CraterLibClientInitializer implements ClientModInitializer { + + @Override + public void onInitializeClient() { + new CraterPacketNetwork(new CraterFabricNetworkHandler(PacketSide.CLIENT)); + ClientTickEvents.START_CLIENT_TICK.register((listener) -> { + if (listener.level == null) + return; + + CraterClientTickEvent event = new CraterClientTickEvent(BridgedClientLevel.of(listener.level)); + CraterEventBus.INSTANCE.postEvent(event); + }); + + CraterEventBus.INSTANCE.registerEventListener(CraterLibClientInitializer.class); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/FabricClientPlatform.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/FabricClientPlatform.java new file mode 100644 index 0000000..7852f12 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/client/FabricClientPlatform.java @@ -0,0 +1,34 @@ +package com.hypherionmc.craterlib.client; + +import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; + +/** + * @author HypherionSA + */ +public class FabricClientPlatform implements ClientPlatform { + + @Override + public BridgedMinecraft getClientInstance() { + return new BridgedMinecraft(); + } + + @Override + public BridgedPlayer getClientPlayer() { + return BridgedPlayer.of(Minecraft.getInstance().player); + } + + @Override + public BridgedClientLevel getClientLevel() { + return BridgedClientLevel.of(Minecraft.getInstance().level); + } + + @Override + public Connection getClientConnection() { + return Minecraft.getInstance().getConnection().getConnection(); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCommonPlatform.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCommonPlatform.java new file mode 100644 index 0000000..c1a30c7 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCommonPlatform.java @@ -0,0 +1,18 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.CommonPlatform; +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import net.minecraft.server.MinecraftServer; + +/** + * @author HypherionSA + */ +public class FabricCommonPlatform implements CommonPlatform { + + public static MinecraftServer server; + + @Override + public BridgedMinecraftServer getMCServer() { + return BridgedMinecraftServer.of(server); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCompatHelper.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCompatHelper.java new file mode 100644 index 0000000..dbcaf63 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricCompatHelper.java @@ -0,0 +1,23 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.compat.FabricTailor; +import com.hypherionmc.craterlib.compat.Vanish; +import com.hypherionmc.craterlib.core.platform.CompatUtils; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; + +public class FabricCompatHelper implements CompatUtils { + + @Override + public boolean isPlayerActive(BridgedPlayer player) { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("melius-vanish")) + return true; + + return Vanish.isPlayerVanished(player.toMojangServerPlayer()); + } + + @Override + public String getSkinUUID(BridgedPlayer player) { + return FabricTailor.getTailorSkin(player.toMojangServerPlayer()); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricLoaderHelper.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricLoaderHelper.java new file mode 100644 index 0000000..95d4d46 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/common/FabricLoaderHelper.java @@ -0,0 +1,75 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.Environment; +import com.hypherionmc.craterlib.core.platform.LoaderType; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; + +import java.io.File; + +/** + * @author HypherionSA + * @date 07/08/2022 + */ +public class FabricLoaderHelper implements ModloaderEnvironment { + + @Override + public boolean isFabric() { + return true; + } + + @Override + public LoaderType getLoaderType() { + return LoaderType.FABRIC; + } + + @Override + public String getGameVersion() { + return SharedConstants.VERSION_STRING; + } + + @Override + public File getGameFolder() { + return Minecraft.getInstance().gameDirectory; + } + + @Override + public File getConfigFolder() { + return FabricLoader.getInstance().getConfigDir().toFile(); + } + + @Override + public File getModsFolder() { + return new File(FabricLoader.getInstance().getGameDir().toString() + File.separator + "mods"); + } + + @Override + public Environment getEnvironment() { + switch (FabricLoader.getInstance().getEnvironmentType()) { + case SERVER -> { + return Environment.SERVER; + } + case CLIENT -> { + return Environment.CLIENT; + } + } + return Environment.UNKNOWN; + } + + @Override + public boolean isModLoaded(String modid) { + return FabricLoader.getInstance().isModLoaded(modid); + } + + @Override + public boolean isDevEnv() { + return FabricLoader.getInstance().isDevelopmentEnvironment(); + } + + @Override + public int getModCount() { + return FabricLoader.getInstance().getAllMods().size(); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/FabricTailor.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/FabricTailor.java new file mode 100644 index 0000000..b95a7d7 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/FabricTailor.java @@ -0,0 +1,23 @@ +package com.hypherionmc.craterlib.compat; + +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import net.minecraft.server.level.ServerPlayer; +import org.samo_lego.fabrictailor.casts.TailoredPlayer; +public class FabricTailor { + + public static String getTailorSkin(ServerPlayer player) { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("fabrictailor")) + return player.getStringUUID(); + + try { + if (player instanceof TailoredPlayer tp) { + return tp.getSkinId(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return player.getStringUUID(); + } + +} \ No newline at end of file diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java new file mode 100644 index 0000000..e75b13d --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java @@ -0,0 +1,25 @@ +package com.hypherionmc.craterlib.compat; + +import com.hypherionmc.craterlib.api.events.server.CraterPlayerEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import me.drex.vanish.api.VanishAPI; +import me.drex.vanish.api.VanishEvents; +import net.minecraft.server.level.ServerPlayer; + +public class Vanish { + + public static void register() { + VanishEvents.VANISH_EVENT.register((serverPlayer, b) -> { + if (b) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedOut(BridgedPlayer.of(serverPlayer), true)); + } else { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedIn(BridgedPlayer.of(serverPlayer), true)); + } + }); + } + + public static boolean isPlayerVanished(ServerPlayer player) { + return VanishAPI.isVanished(player); + } +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java new file mode 100644 index 0000000..576a276 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -0,0 +1,37 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.CraterServerChatEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.PlayerChatMessage; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.FilteredText; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +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(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) +public class ServerGamePacketListenerImplMixin { + + @Shadow + public ServerPlayer player; + + @Inject( + method = "lambda$handleChat$5", + at = @At("HEAD"), + 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)); + CraterEventBus.INSTANCE.postEvent(event); + if (event.wasCancelled()) + ci.cancel(); + } + +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..f3dc866 --- /dev/null +++ b/1.21.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.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/TutorialMixin.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/TutorialMixin.java new file mode 100644 index 0000000..f9eaa50 --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/mixin/TutorialMixin.java @@ -0,0 +1,24 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.BridgedOptions; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.client.tutorial.Tutorial; +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.CallbackInfo; + +@Mixin(Tutorial.class) +public class TutorialMixin { + + @Inject(method = "", at = @At("RETURN")) + private void injectEarlyInitEvent(Minecraft minecraft, Options options, CallbackInfo ci) { + LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(options)); + CraterEventBus.INSTANCE.postEvent(event); + } + +} diff --git a/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/network/CraterFabricNetworkHandler.java b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/network/CraterFabricNetworkHandler.java new file mode 100644 index 0000000..cd8740b --- /dev/null +++ b/1.21.2/Fabric/src/main/java/com/hypherionmc/craterlib/network/CraterFabricNetworkHandler.java @@ -0,0 +1,68 @@ +package com.hypherionmc.craterlib.network; + +import com.hypherionmc.craterlib.api.networking.CommonPacketWrapper; +import com.hypherionmc.craterlib.core.networking.PacketRegistry; +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public class CraterFabricNetworkHandler extends PacketRegistry { + + public CraterFabricNetworkHandler(PacketSide side) { + super(side); + } + + protected void registerPacket(PacketHolder holder) { + try + { + PayloadTypeRegistry.playC2S().register(holder.getType(), holder.getCodec()); + PayloadTypeRegistry.playS2C().register(holder.getType(), holder.getCodec()); + } + catch (IllegalArgumentException e) + { + // do nothing + } + + if (PacketSide.CLIENT.equals(this.side)) { + ClientPlayNetworking.registerGlobalReceiver(holder.getType(), + (ClientPlayNetworking.PlayPayloadHandler>) (payload, context) -> context.client().execute(() -> + holder.handler().accept( + new PacketContext<>(payload.packet(), side)))); + } + + ServerPlayNetworking.registerGlobalReceiver(holder.getType(), + (ServerPlayNetworking.PlayPayloadHandler>) (payload, context) -> context.player().server.execute(() -> + holder.handler().accept( + new PacketContext<>(BridgedPlayer.of(context.player()), payload.packet(), side)))); + } + + public void sendToServer(T packet) { + this.sendToServer(packet, false); + } + + public void sendToServer(T packet, boolean ignoreCheck) { + PacketHolder container = (PacketHolder) PACKET_MAP.get(packet.getClass()); + + if (container != null) { + if (ignoreCheck || ClientPlayNetworking.canSend(container.type().id())) { + ClientPlayNetworking.send(new CommonPacketWrapper<>(container, packet)); + } + } + } + + public void sendToClient(T packet, BridgedPlayer player) { + PacketHolder container = (PacketHolder) PACKET_MAP.get(packet.getClass()); + if (container != null) { + if (ServerPlayNetworking.canSend(player.toMojangServerPlayer(), container.type().id())) { + ServerPlayNetworking.send(player.toMojangServerPlayer(), new CommonPacketWrapper<>(container, packet)); + } + } + } +} diff --git a/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform new file mode 100644 index 0000000..a78d9e5 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.client.FabricClientPlatform diff --git a/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform new file mode 100644 index 0000000..9a2fdb0 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.FabricCommonPlatform diff --git a/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils new file mode 100644 index 0000000..62f79a8 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.FabricCompatHelper \ No newline at end of file diff --git a/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment new file mode 100644 index 0000000..9a1fb33 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.FabricLoaderHelper diff --git a/1.21.2/Fabric/src/main/resources/assets/craterlib/craterlib_logo.png b/1.21.2/Fabric/src/main/resources/assets/craterlib/craterlib_logo.png new file mode 100644 index 0000000..ce0159c Binary files /dev/null and b/1.21.2/Fabric/src/main/resources/assets/craterlib/craterlib_logo.png differ diff --git a/1.21.2/Fabric/src/main/resources/craterlib.fabric.mixins.json b/1.21.2/Fabric/src/main/resources/craterlib.fabric.mixins.json new file mode 100644 index 0000000..a6d0bc1 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/craterlib.fabric.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.hypherionmc.craterlib.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "TutorialMixin" + ], + "server": [ + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.2/Fabric/src/main/resources/fabric.mod.json b/1.21.2/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..9e98976 --- /dev/null +++ b/1.21.2/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,39 @@ +{ + "schemaVersion": 1, + "id": "${mod_id}", + "version": "${version}", + "name": "${mod_name}", + "description": "A library mod used by First Dark Development and HypherionSA Mods", + "authors": [ + "${mod_author}", + "Misha" + ], + "contact": { + "homepage": "https://modrinth.com/mod/craterlib", + "sources": "https://github.com/firstdarkdev/craterLib/" + }, + "license": "MIT", + "icon": "assets/craterlib/craterlib_logo.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.hypherionmc.craterlib.CraterLibInitializer" + ], + "client": [ + "com.hypherionmc.craterlib.client.CraterLibClientInitializer" + ], + "modmenu": [ + "com.hypherionmc.craterlib.CraterLibModMenuIntegration" + ] + }, + "mixins": [ + "${mod_id}.mixins.json", + "${mod_id}.fabric.mixins.json" + ], + "depends": { + "fabricloader": ">=0.16.7", + "fabric-api": "*", + "minecraft": ">=1.21.2", + "java": ">=21" + } +} diff --git a/1.21.2/Forge/build.gradle b/1.21.2/Forge/build.gradle new file mode 100644 index 0000000..b117838 --- /dev/null +++ b/1.21.2/Forge/build.gradle @@ -0,0 +1,116 @@ +// Adjust the output jar name here +archivesBaseName = "${mod_name.replace(" ", "")}-Forge-${minecraft_version}" + +dependencies { + // Compat + // NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}") + + // Do not edit or remove + implementation project(":Common") +} + +shadowJar { + from sourceSets.main.output + configurations = [project.configurations.shade] + + dependencies { + exclude(dependency('com.google.code.gson:.*')) + + relocate 'me.hypherionmc.moonconfig', 'shadow.hypherionmc.moonconfig' + relocate 'me.hypherionmc.mcdiscordformatter', 'shadow.hypherionmc.mcdiscordformatter' + relocate 'net.kyori', 'shadow.kyori' + } + + setArchiveClassifier('dev-shadow') + mergeServiceFiles() +} + +/** + * =============================================================================== + * = DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING = + * =============================================================================== + */ + +unimined.minecraft { + minecraftForge { + loader forge_version + mixinConfig("${mod_id}.mixins.json", "${mod_id}.forge.mixins.json") + } +} + +remapJar { + inputFile.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier.set null +} + +jar { + archiveClassifier.set "dev" +} + +processResources { + from project(":Common").sourceSets.main.resources + def buildProps = project.properties.clone() + + filesMatching("META-INF/mods.toml") { + expand buildProps + } +} + +compileTestJava.enabled = false + +tasks.withType(JavaCompile).configureEach { + source(project(":Common").sourceSets.main.allSource) +} + +/** + * Publishing Config + */ +publishing { + publications { + mavenJava(MavenPublication) { + artifactId project.archivesBaseName + from components.java + + artifact(remapJar) { + builtBy remapJar + } + + pom.withXml { + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + it.artifactId.text() == 'regutils-joined-fabric' || + it.artifactId.text() == 'core' || + it.artifactId.text() == 'toml' + }.each() { + it.parent().remove(it) + } + } + } + } + + repositories { + maven rootProject.orion.getPublishingMaven() + } +} + +publisher { + apiKeys { + modrinth(System.getenv("MODRINTH_TOKEN")) + curseforge(System.getenv("CURSE_TOKEN")) + nightbloom(System.getenv("PLATFORM_KEY")) + } + + setCurseID(curse_id) + setModrinthID(modrinth_id) + setNightbloomID("craterlib") + setVersionType("release") + setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") + setProjectVersion("${minecraft_version}-${project.version}") + setDisplayName("[Forge 1.20.6] CraterLib - ${project.version}") + setGameVersions("1.20.6") + setLoaders("forge") + setArtifact(remapJar) + setCurseEnvironment("both") + setIsManualRelease(true) +} \ No newline at end of file diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/CraterLib.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/CraterLib.java new file mode 100644 index 0000000..754d986 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/CraterLib.java @@ -0,0 +1,36 @@ +package com.hypherionmc.craterlib; + +import com.hypherionmc.craterlib.api.events.client.LateInitEvent; +import com.hypherionmc.craterlib.common.ForgeServerEvents; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +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.CraterForgeNetworkHandler; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.BridgedOptions; +import net.minecraft.client.Minecraft; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLLoader; + +@Mod(CraterConstants.MOD_ID) +public class CraterLib { + + public CraterLib() { + MinecraftForge.EVENT_BUS.register(new ForgeServerEvents()); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup); + } + + public void commonSetup(FMLCommonSetupEvent evt) { + new CraterPacketNetwork(new CraterForgeNetworkHandler(FMLLoader.getDist().isClient() ? PacketSide.CLIENT : PacketSide.SERVER)); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { + LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); + CraterEventBus.INSTANCE.postEvent(event); + }); + } +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientEvents.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientEvents.java new file mode 100644 index 0000000..cb096d5 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientEvents.java @@ -0,0 +1,25 @@ +package com.hypherionmc.craterlib.client; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.api.events.client.CraterClientTickEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import net.minecraft.client.Minecraft; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = CraterConstants.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT) +public class ForgeClientEvents { + + @SubscribeEvent + public static void clientTick(TickEvent.LevelTickEvent event) { + if (Minecraft.getInstance().level == null) + return; + + CraterClientTickEvent craterClientTickEvent = new CraterClientTickEvent(BridgedClientLevel.of(Minecraft.getInstance().level)); + CraterEventBus.INSTANCE.postEvent(craterClientTickEvent); + } + +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientHelper.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientHelper.java new file mode 100644 index 0000000..6776622 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/client/ForgeClientHelper.java @@ -0,0 +1,41 @@ +package com.hypherionmc.craterlib.client; + +import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; + +import java.util.Objects; + +/** + * @author HypherionSA + * @date 16/06/2022 + */ +public class ForgeClientHelper implements ClientPlatform { + + public ForgeClientHelper() { + } + + @Override + public BridgedMinecraft getClientInstance() { + return new BridgedMinecraft(); + } + + @Override + public BridgedPlayer getClientPlayer() { + return BridgedPlayer.of(Minecraft.getInstance().player); + } + + @Override + public BridgedClientLevel getClientLevel() { + return BridgedClientLevel.of(Minecraft.getInstance().level); + } + + @Override + public Connection getClientConnection() { + Objects.requireNonNull(Minecraft.getInstance().getConnection(), "Cannot send packets when not in game!"); + return Minecraft.getInstance().getConnection().getConnection(); + } +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCommonHelper.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCommonHelper.java new file mode 100644 index 0000000..281ddd7 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCommonHelper.java @@ -0,0 +1,26 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.CommonPlatform; +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraftforge.server.ServerLifecycleHooks; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author HypherionSA + */ +public class ForgeCommonHelper implements CommonPlatform { + + public static Map TABS = new HashMap<>(); + + public ForgeCommonHelper() { + } + + @Override + public BridgedMinecraftServer getMCServer() { + return BridgedMinecraftServer.of(ServerLifecycleHooks.getCurrentServer()); + } +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCompatHelper.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCompatHelper.java new file mode 100644 index 0000000..f325837 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeCompatHelper.java @@ -0,0 +1,17 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.CompatUtils; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; + +public class ForgeCompatHelper implements CompatUtils { + + @Override + public boolean isPlayerActive(BridgedPlayer player) { + return true; + } + + @Override + public String getSkinUUID(BridgedPlayer player) { + return player.getStringUUID(); + } +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeLoaderHelper.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeLoaderHelper.java new file mode 100644 index 0000000..6f15754 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeLoaderHelper.java @@ -0,0 +1,79 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.Environment; +import com.hypherionmc.craterlib.core.platform.LoaderType; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.FMLPaths; + +import java.io.File; + +/** + * @author HypherionSA + */ +public class ForgeLoaderHelper implements ModloaderEnvironment { + + public ForgeLoaderHelper() { + } + + @Override + public boolean isFabric() { + return false; + } + + @Override + public LoaderType getLoaderType() { + return LoaderType.FORGE; + } + + @Override + public String getGameVersion() { + return SharedConstants.VERSION_STRING; + } + + @Override + public File getGameFolder() { + return Minecraft.getInstance().gameDirectory; + } + + @Override + public File getConfigFolder() { + return FMLPaths.CONFIGDIR.get().toFile(); + } + + @Override + public File getModsFolder() { + return FMLPaths.MODSDIR.get().toFile(); + } + + @Override + public Environment getEnvironment() { + switch (FMLLoader.getDist()) { + case CLIENT -> { + return Environment.CLIENT; + } + case DEDICATED_SERVER -> { + return Environment.SERVER; + } + } + return Environment.UNKNOWN; + } + + @Override + public boolean isModLoaded(String modid) { + return ModList.get().isLoaded(modid); + } + + @Override + public boolean isDevEnv() { + return !FMLLoader.isProduction(); + } + + @Override + public int getModCount() { + return ModList.get().size(); + } +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java new file mode 100644 index 0000000..f354ddc --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/common/ForgeServerEvents.java @@ -0,0 +1,43 @@ +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; +import net.minecraftforge.event.server.ServerStartingEvent; +import net.minecraftforge.event.server.ServerStoppedEvent; +import net.minecraftforge.event.server.ServerStoppingEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public class ForgeServerEvents { + + @SubscribeEvent + public void serverStarting(ServerStartingEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Starting(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStarted(ServerStartedEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Started(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStopping(ServerStoppingEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopping(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStopped(ServerStoppedEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopped(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent()); + CommandsRegistry.INSTANCE.registerCommands(event.getDispatcher()); + } + +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java new file mode 100644 index 0000000..927bd23 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ConfigScreenHandlerMixin.java @@ -0,0 +1,43 @@ +package com.hypherionmc.craterlib.mixin; + +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 net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraftforge.client.ConfigScreenHandler; +import net.minecraftforge.forgespi.language.IModInfo; +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; +import java.util.function.BiFunction; + +/** + * @author HypherionSA + */ +@Mixin(ConfigScreenHandler.class) +public class ConfigScreenHandlerMixin { + + /** + * Inject Auto Generated config Screens into forge + * + */ + @Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false) + private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable>> cir) { + ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> { + if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { + ModuleConfig config = (ModuleConfig) conf; + if (config.getModId().equals(selectedMod.getModId())) { + cir.setReturnValue( + Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen)) + ); + } + } + }); + } + +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java new file mode 100644 index 0000000..012b6ff --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -0,0 +1,37 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.CraterServerChatEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.PlayerChatMessage; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.FilteredText; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +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(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) +public class ServerGamePacketListenerImplMixin { + + @Shadow + public ServerPlayer player; + + @Inject( + method = "lambda$handleChat$5", + at = @At("HEAD"), + 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)); + CraterEventBus.INSTANCE.postEvent(event); + if (event.wasCancelled()) + ci.cancel(); + } + +} diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..cb697db --- /dev/null +++ b/1.21.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.isModded() + ) + )); + } + } + +} \ No newline at end of file diff --git a/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/network/CraterForgeNetworkHandler.java b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/network/CraterForgeNetworkHandler.java new file mode 100644 index 0000000..cc14988 --- /dev/null +++ b/1.21.2/Forge/src/main/java/com/hypherionmc/craterlib/network/CraterForgeNetworkHandler.java @@ -0,0 +1,98 @@ +package com.hypherionmc.craterlib.network; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.core.networking.PacketRegistry; +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraftforge.event.network.CustomPayloadEvent; +import net.minecraftforge.network.ChannelBuilder; +import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.network.SimpleChannel; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public class CraterForgeNetworkHandler extends PacketRegistry { + private final Map, SimpleChannel> CHANNELS = new HashMap<>(); + + public CraterForgeNetworkHandler(PacketSide side) { + super(side); + } + + protected void registerPacket(PacketHolder holder) { + if (CHANNELS.get(holder.messageType()) == null) { + SimpleChannel channel = ChannelBuilder + .named(holder.type().id()) + .clientAcceptedVersions((a, b) -> true) + .serverAcceptedVersions((a, b) -> true) + .networkProtocolVersion(1) + .simpleChannel(); + + channel.messageBuilder(holder.messageType()) + .decoder(mojangDecoder(holder.decoder())) + .encoder(mojangEncoder(holder.encoder())) + .consumerNetworkThread(buildHandler(holder.handler())) + .add(); + + CHANNELS.put(holder.messageType(), channel); + } else { + CraterConstants.LOG.error("Trying to register duplicate packet for type {}", holder.messageType()); + } + } + + public void sendToServer(T packet) { + this.sendToServer(packet, false); + } + + public void sendToServer(T packet, boolean ignoreCheck) { + SimpleChannel channel = CHANNELS.get(packet.getClass()); + Connection connection = Minecraft.getInstance().getConnection().getConnection(); + if (channel.isRemotePresent(connection) || ignoreCheck) { + channel.send(packet, PacketDistributor.SERVER.noArg()); + } + } + + public void sendToClient(T packet, BridgedPlayer player) { + SimpleChannel channel = CHANNELS.get(packet.getClass()); + ServerGamePacketListenerImpl connection = player.getConnection(); + if (connection == null) + return; + + if (channel.isRemotePresent(connection.getConnection())) { + channel.send(packet, PacketDistributor.PLAYER.with(player.toMojangServerPlayer())); + } + } + + private Function mojangDecoder(Function handler) { + return byteBuf -> handler.apply(BridgedFriendlyByteBuf.of(byteBuf)); + } + + private BiConsumer mojangEncoder(BiConsumer handler) { + return ((t, byteBuf) -> handler.accept(t, BridgedFriendlyByteBuf.of(byteBuf))); + } + + private BiConsumer buildHandler(Consumer> handler) { + return (message, ctx) -> { + ctx.enqueueWork(() -> { + PacketSide side = ctx.getDirection().getReceptionSide().isServer() ? PacketSide.SERVER : PacketSide.CLIENT; + ServerPlayer player = ctx.getSender(); + handler.accept(new PacketContext<>(BridgedPlayer.of(player), message, side)); + }); + ctx.setPacketHandled(true); + }; + } +} \ No newline at end of file diff --git a/1.21.2/Forge/src/main/resources/META-INF/mods.toml b/1.21.2/Forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..c2c7608 --- /dev/null +++ b/1.21.2/Forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,31 @@ +modLoader = "javafml" +loaderVersion = "[50,)" +license = "MIT" +issueTrackerURL = "https://github.com/firstdarkdev/craterLib/issues" + +[[mods]] + modId = "${mod_id}" + version = "${version}" + displayName = "${mod_name}" + displayURL = "https://modrinth.com/mod/craterlib" + logoFile = "craterlib_logo.png" + #credits="Thanks for this example mod goes to Java" + authors = "${mod_author}, Zenith" + description = ''' + A library mod used by First Dark Development and HypherionSA Mods + ''' + displayTest = "NONE" + +[[dependencies.${ mod_id }]] + modId = "forge" + mandatory = true + versionRange = "[50,)" + ordering = "NONE" + side = "BOTH" + +[[dependencies.${ mod_id }]] + modId = "minecraft" + mandatory = true + versionRange = "[1.20.6,1.21)" + ordering = "NONE" + side = "BOTH" diff --git a/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform new file mode 100644 index 0000000..a12ad8c --- /dev/null +++ b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.client.ForgeClientHelper \ No newline at end of file diff --git a/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform new file mode 100644 index 0000000..09e119f --- /dev/null +++ b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.ForgeCommonHelper \ No newline at end of file diff --git a/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils new file mode 100644 index 0000000..a9f823d --- /dev/null +++ b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.ForgeCompatHelper \ No newline at end of file diff --git a/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment new file mode 100644 index 0000000..02b4e07 --- /dev/null +++ b/1.21.2/Forge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.ForgeLoaderHelper \ No newline at end of file diff --git a/1.21.2/Forge/src/main/resources/craterlib.forge.mixins.json b/1.21.2/Forge/src/main/resources/craterlib.forge.mixins.json new file mode 100644 index 0000000..892f6d7 --- /dev/null +++ b/1.21.2/Forge/src/main/resources/craterlib.forge.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.hypherionmc.craterlib.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "ConfigScreenHandlerMixin" + ], + "server": [ + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.2/Forge/src/main/resources/craterlib_logo.png b/1.21.2/Forge/src/main/resources/craterlib_logo.png new file mode 100644 index 0000000..ce0159c Binary files /dev/null and b/1.21.2/Forge/src/main/resources/craterlib_logo.png differ diff --git a/1.21.2/LICENSE b/1.21.2/LICENSE new file mode 100644 index 0000000..f4a1e2d --- /dev/null +++ b/1.21.2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) HypherionSA and Contributors 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/1.21.2/NeoForge/build.gradle b/1.21.2/NeoForge/build.gradle new file mode 100644 index 0000000..9dfa4ab --- /dev/null +++ b/1.21.2/NeoForge/build.gradle @@ -0,0 +1,117 @@ +archivesBaseName = "${mod_name.replace(" ", "")}-NeoForge-${minecraft_version}" + +dependencies { + // Compat + modImplementation("maven.modrinth:vanishmod:${vanishmod_neo}") + + stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}") + + // Do not edit or remove + implementation project(":Common") +} + +shadowJar { + from sourceSets.main.output + configurations = [project.configurations.shade] + + dependencies { + exclude(dependency('com.google.code.gson:.*')) + + relocate 'me.hypherionmc.moonconfig', 'shadow.hypherionmc.moonconfig' + relocate 'me.hypherionmc.mcdiscordformatter', 'shadow.hypherionmc.mcdiscordformatter' + relocate 'net.kyori', 'shadow.kyori' + } + + setArchiveClassifier('dev-shadow') + mergeServiceFiles() +} + +/** + * =============================================================================== + * = DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING = + * =============================================================================== + */ + +unimined.minecraft { + neoForged { + loader neoforge_version + mixinConfig("${mod_id}.mixins.json", "${mod_id}.neoforge.mixins.json") + } +} + +remapJar { + inputFile.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier.set null +} + +jar { + archiveClassifier.set "dev" +} + +processResources { + from project(":Common").sourceSets.main.resources + def buildProps = project.properties.clone() + + filesMatching("META-INF/neoforge.mods.toml") { + expand buildProps + } +} + +compileTestJava.enabled = false + +tasks.withType(JavaCompile).configureEach { + source(project(":Common").sourceSets.main.allSource) +} + +/** + * Publishing Config + */ +publishing { + publications { + mavenJava(MavenPublication) { + artifactId project.archivesBaseName + from components.java + + artifact(remapJar) { + builtBy remapJar + } + + pom.withXml { + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + it.artifactId.text() == 'regutils-joined-fabric' || + it.artifactId.text() == 'core' || + it.artifactId.text() == 'toml' + }.each() { + it.parent().remove(it) + } + } + } + } + + repositories { + maven rootProject.orion.getPublishingMaven() + } +} + +publisher { + apiKeys { + modrinth(System.getenv("MODRINTH_TOKEN")) + curseforge(System.getenv("CURSE_TOKEN")) + nightbloom(System.getenv("PLATFORM_KEY")) + } + + setCurseID(curse_id) + setModrinthID(modrinth_id) + setNightbloomID("craterlib") + setVersionType("release") + setChangelog("https://raw.githubusercontent.com/hypherionmc/changelogs/main/craterlib/changelog-forge.md") + setProjectVersion("${minecraft_version}-${project.version}") + setDisplayName("[NeoForge 1.21.2] CraterLib - ${project.version}") + setGameVersions("1.21.2") + setLoaders("neoforge") + setArtifact(remapJar) + setCurseEnvironment("both") + setIsManualRelease(true) +} \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/CraterLib.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/CraterLib.java new file mode 100644 index 0000000..566e4bf --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/CraterLib.java @@ -0,0 +1,41 @@ +package com.hypherionmc.craterlib; + +import com.hypherionmc.craterlib.client.NeoForgeClientHelper; +import com.hypherionmc.craterlib.common.NeoForgeServerEvents; +import com.hypherionmc.craterlib.compat.Vanish; +import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; +import com.hypherionmc.craterlib.core.networking.PacketRegistry; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import com.hypherionmc.craterlib.network.CraterNeoForgeNetworkHandler; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.neoforge.common.NeoForge; + +@Mod(CraterConstants.MOD_ID) +public class CraterLib { + + private final PacketRegistry handler; + + public CraterLib(IEventBus eventBus) { + NeoForge.EVENT_BUS.register(new NeoForgeServerEvents()); + eventBus.addListener(this::commonSetup); + eventBus.addListener(this::clientSetup); + handler = new CraterNeoForgeNetworkHandler(FMLLoader.getDist().isClient() ? PacketSide.CLIENT : PacketSide.SERVER); + + if (ModloaderEnvironment.INSTANCE.isModLoaded("vmod")) { + NeoForge.EVENT_BUS.register(new Vanish()); + } + } + + public void commonSetup(FMLCommonSetupEvent evt) { + new CraterPacketNetwork(handler); + } + + public void clientSetup(FMLClientSetupEvent evt) { + NeoForgeClientHelper.registerClient(); + } +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientEvents.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientEvents.java new file mode 100644 index 0000000..3e6455a --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientEvents.java @@ -0,0 +1,25 @@ +package com.hypherionmc.craterlib.client; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.api.events.client.CraterClientTickEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import net.minecraft.client.Minecraft; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.tick.LevelTickEvent; + +@EventBusSubscriber(modid = CraterConstants.MOD_ID, value = Dist.CLIENT) +public class NeoForgeClientEvents { + + @SubscribeEvent + public static void clientTick(LevelTickEvent.Pre event) { + if (Minecraft.getInstance().level == null) + return; + + CraterClientTickEvent craterClientTickEvent = new CraterClientTickEvent(BridgedClientLevel.of(Minecraft.getInstance().level)); + CraterEventBus.INSTANCE.postEvent(craterClientTickEvent); + } + +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java new file mode 100644 index 0000000..e7d9013 --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/client/NeoForgeClientHelper.java @@ -0,0 +1,61 @@ +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.annotations.NoConfigScreen; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.core.platform.ClientPlatform; +import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft; +import com.hypherionmc.craterlib.nojang.client.BridgedOptions; +import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.minecraft.client.Minecraft; +import net.minecraft.network.Connection; +import net.neoforged.fml.ModList; +import net.neoforged.neoforge.client.gui.IConfigScreenFactory; + +import java.util.Objects; + +/** + * @author HypherionSA + */ +public class NeoForgeClientHelper implements ClientPlatform { + + public NeoForgeClientHelper() { + } + + @Override + public BridgedMinecraft getClientInstance() { + return new BridgedMinecraft(); + } + + @Override + public BridgedPlayer getClientPlayer() { + return BridgedPlayer.of(Minecraft.getInstance().player); + } + + @Override + public BridgedClientLevel getClientLevel() { + return BridgedClientLevel.of(Minecraft.getInstance().level); + } + + @Override + public Connection getClientConnection() { + Objects.requireNonNull(Minecraft.getInstance().getConnection(), "Cannot send packets when not in game!"); + return Minecraft.getInstance().getConnection().getConnection(); + } + + public static void registerClient() { + LateInitEvent event = new LateInitEvent(new BridgedMinecraft(), BridgedOptions.of(Minecraft.getInstance().options)); + CraterEventBus.INSTANCE.postEvent(event); + + ConfigController.getWatchedConfigs().forEach((conf, watcher) -> { + if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) { + AbstractConfig config = watcher.getLeft(); + ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen)))); + } + }); + } +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCommonHelper.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCommonHelper.java new file mode 100644 index 0000000..50622a8 --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCommonHelper.java @@ -0,0 +1,19 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.CommonPlatform; +import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +/** + * @author HypherionSA + */ +public class NeoForgeCommonHelper implements CommonPlatform { + + public NeoForgeCommonHelper() { + } + + @Override + public BridgedMinecraftServer getMCServer() { + return BridgedMinecraftServer.of(ServerLifecycleHooks.getCurrentServer()); + } +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCompatHelper.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCompatHelper.java new file mode 100644 index 0000000..469c172 --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeCompatHelper.java @@ -0,0 +1,22 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.CompatUtils; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import redstonedubstep.mods.vanishmod.VanishUtil; + +public class NeoForgeCompatHelper implements CompatUtils { + + @Override + public boolean isPlayerActive(BridgedPlayer player) { + if (!ModloaderEnvironment.INSTANCE.isModLoaded("vmod")) + return true; + + return VanishUtil.isVanished(player.toMojangServerPlayer()); + } + + @Override + public String getSkinUUID(BridgedPlayer player) { + return player.getStringUUID(); + } +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeLoaderHelper.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeLoaderHelper.java new file mode 100644 index 0000000..6fc2925 --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeLoaderHelper.java @@ -0,0 +1,79 @@ +package com.hypherionmc.craterlib.common; + +import com.hypherionmc.craterlib.core.platform.Environment; +import com.hypherionmc.craterlib.core.platform.LoaderType; +import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; +import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.FMLPaths; + +import java.io.File; + +/** + * @author HypherionSA + */ +public class NeoForgeLoaderHelper implements ModloaderEnvironment { + + public NeoForgeLoaderHelper() { + } + + @Override + public boolean isFabric() { + return false; + } + + @Override + public LoaderType getLoaderType() { + return LoaderType.NEOFORGE; + } + + @Override + public String getGameVersion() { + return SharedConstants.VERSION_STRING; + } + + @Override + public File getGameFolder() { + return Minecraft.getInstance().gameDirectory; + } + + @Override + public File getConfigFolder() { + return FMLPaths.CONFIGDIR.get().toFile(); + } + + @Override + public File getModsFolder() { + return FMLPaths.MODSDIR.get().toFile(); + } + + @Override + public Environment getEnvironment() { + switch (FMLLoader.getDist()) { + case CLIENT -> { + return Environment.CLIENT; + } + case DEDICATED_SERVER -> { + return Environment.SERVER; + } + } + return Environment.UNKNOWN; + } + + @Override + public boolean isModLoaded(String modid) { + return ModList.get().isLoaded(modid); + } + + @Override + public boolean isDevEnv() { + return !FMLLoader.isProduction(); + } + + @Override + public int getModCount() { + return ModList.get().size(); + } +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java new file mode 100644 index 0000000..868b89f --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/common/NeoForgeServerEvents.java @@ -0,0 +1,41 @@ +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.server.BridgedMinecraftServer; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; + +public class NeoForgeServerEvents { + + @SubscribeEvent + public void serverStarting(ServerStartingEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Starting(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStarted(ServerStartedEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Started(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStopping(ServerStoppingEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopping(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void serverStopped(ServerStoppedEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterServerLifecycleEvent.Stopped(BridgedMinecraftServer.of(event.getServer()))); + } + + @SubscribeEvent + public void onCommandRegister(RegisterCommandsEvent event) { + CraterEventBus.INSTANCE.postEvent(new CraterRegisterCommandEvent(event.getDispatcher())); + } + +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java new file mode 100644 index 0000000..8d19f4c --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/compat/Vanish.java @@ -0,0 +1,24 @@ +package com.hypherionmc.craterlib.compat; + +import com.hypherionmc.craterlib.api.events.server.CraterPlayerEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.neoforged.bus.api.SubscribeEvent; +import redstonedubstep.mods.vanishmod.api.PlayerVanishEvent; + +public class Vanish { + + public Vanish() { + + } + + @SubscribeEvent + public void vanishevent(PlayerVanishEvent event) { + if (event.isVanished()) { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedOut(BridgedPlayer.of(event.getEntity()), true)); + } else { + CraterEventBus.INSTANCE.postEvent(new CraterPlayerEvent.PlayerLoggedIn(BridgedPlayer.of(event.getEntity()), true)); + } + } + +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java new file mode 100644 index 0000000..012b6ff --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerGamePacketListenerImplMixin.java @@ -0,0 +1,37 @@ +package com.hypherionmc.craterlib.mixin; + +import com.hypherionmc.craterlib.api.events.server.CraterServerChatEvent; +import com.hypherionmc.craterlib.core.event.CraterEventBus; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import com.hypherionmc.craterlib.utils.ChatUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.PlayerChatMessage; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.FilteredText; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +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(value = ServerGamePacketListenerImpl.class, priority = Integer.MIN_VALUE) +public class ServerGamePacketListenerImplMixin { + + @Shadow + public ServerPlayer player; + + @Inject( + method = "lambda$handleChat$5", + at = @At("HEAD"), + 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)); + CraterEventBus.INSTANCE.postEvent(event); + if (event.wasCancelled()) + ci.cancel(); + } + +} diff --git a/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/mixin/ServerStatusPacketListenerMixin.java new file mode 100644 index 0000000..cb697db --- /dev/null +++ b/1.21.2/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.2/NeoForge/src/main/java/com/hypherionmc/craterlib/network/CraterNeoForgeNetworkHandler.java b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/network/CraterNeoForgeNetworkHandler.java new file mode 100644 index 0000000..59cb127 --- /dev/null +++ b/1.21.2/NeoForge/src/main/java/com/hypherionmc/craterlib/network/CraterNeoForgeNetworkHandler.java @@ -0,0 +1,77 @@ +package com.hypherionmc.craterlib.network; + +import com.hypherionmc.craterlib.CraterConstants; +import com.hypherionmc.craterlib.api.networking.CommonPacketWrapper; +import com.hypherionmc.craterlib.core.networking.PacketRegistry; +import com.hypherionmc.craterlib.core.networking.data.PacketContext; +import com.hypherionmc.craterlib.core.networking.data.PacketHolder; +import com.hypherionmc.craterlib.core.networking.data.PacketSide; +import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +import java.util.function.Consumer; + +/** + * Based on https://github.com/mysticdrew/common-networking/tree/1.20.4 + */ +public class CraterNeoForgeNetworkHandler extends PacketRegistry { + + public CraterNeoForgeNetworkHandler(PacketSide side) { + super(side); + } + + @SubscribeEvent + public void register(final RegisterPayloadHandlersEvent event) { + if (!PACKET_MAP.isEmpty()) { + PACKET_MAP.forEach((type, container) -> event.registrar(container.getType().id().getNamespace()) + .optional().commonBidirectional(container.getType(), container.getCodec(), buildHandler(container.handler()))); + } + } + + @Override + protected void registerPacket(PacketHolder container) { + + } + + public void sendToServer(T packet) { + this.sendToServer(packet, false); + } + + public void sendToServer(T packet, boolean ignoreCheck) { + PacketHolder container = (PacketHolder) PACKET_MAP.get(packet.getClass()); + if (container != null) { + PacketDistributor.sendToServer(new CommonPacketWrapper<>(container, packet)); + } + } + + public void sendToClient(T packet, BridgedPlayer player) { + PacketHolder container = (PacketHolder) PACKET_MAP.get(packet.getClass()); + if (container != null) { + if (player.getConnection().hasChannel(container.type())) { + PacketDistributor.sendToPlayer(player.toMojangServerPlayer(), new CommonPacketWrapper<>(container, packet)); + } + } + } + + private > IPayloadHandler buildHandler(Consumer> handler) { + return (payload, ctx) -> { + try + { + PacketSide side = ctx.flow().getReceptionSide().equals(LogicalSide.SERVER) ? PacketSide.SERVER : PacketSide.CLIENT; + if (PacketSide.SERVER.equals(side)) { + handler.accept(new PacketContext<>(BridgedPlayer.of(ctx.player()), payload.packet(), side)); + } else { + handler.accept(new PacketContext<>(payload.packet(), side)); + } + + } + catch (Throwable t) { + CraterConstants.LOG.error("Error handling packet: {} -> ", payload.packet().getClass(), t); + } + }; + } +} \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml b/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..873731d --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,31 @@ +modLoader = "javafml" +loaderVersion = "[1,)" +license = "MIT" +issueTrackerURL = "https://github.com/firstdarkdev/craterLib/issues" + +[[mods]] + modId = "${mod_id}" + version = "${version}" + displayName = "${mod_name}" + displayURL = "https://modrinth.com/mod/craterlib" + logoFile = "craterlib_logo.png" + #credits="Thanks for this example mod goes to Java" + authors = "${mod_author}, Zenith" + description = ''' + A library mod used by First Dark Development and HypherionSA Mods + ''' + displayTest = "NONE" + +[[dependencies.${ mod_id }]] + modId = "neoforge" + type="required" + versionRange = "[21.2.0-beta,)" + ordering = "NONE" + side = "BOTH" + +[[dependencies.${ mod_id }]] + modId = "minecraft" + type="required" + versionRange = "[1.21.2,)" + ordering = "NONE" + side = "BOTH" diff --git a/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform new file mode 100644 index 0000000..5bd719f --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ClientPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.client.NeoForgeClientHelper \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform new file mode 100644 index 0000000..6cb6efb --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CommonPlatform @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.NeoForgeCommonHelper \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils new file mode 100644 index 0000000..9475f11 --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.CompatUtils @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.NeoForgeCompatHelper \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment new file mode 100644 index 0000000..9a41f05 --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/META-INF/services/com.hypherionmc.craterlib.core.platform.ModloaderEnvironment @@ -0,0 +1 @@ +com.hypherionmc.craterlib.common.NeoForgeLoaderHelper \ No newline at end of file diff --git a/1.21.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json b/1.21.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json new file mode 100644 index 0000000..147f7e0 --- /dev/null +++ b/1.21.2/NeoForge/src/main/resources/craterlib.neoforge.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.hypherionmc.craterlib.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [], + "server": [ + "ServerGamePacketListenerImplMixin", + "ServerStatusPacketListenerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.2/NeoForge/src/main/resources/craterlib_logo.png b/1.21.2/NeoForge/src/main/resources/craterlib_logo.png new file mode 100644 index 0000000..ce0159c Binary files /dev/null and b/1.21.2/NeoForge/src/main/resources/craterlib_logo.png differ diff --git a/1.21.2/README.md b/1.21.2/README.md new file mode 100644 index 0000000..3d8f0a8 --- /dev/null +++ b/1.21.2/README.md @@ -0,0 +1,56 @@ +# CraterLib + +![badge-snapshot](https://maven.firstdarkdev.xyz/api/badge/latest/snapshots/me/hypherionmc/craterlib/CraterLib-common-1.20-pre6?color=40c14a&name=CraterLib-Snapshot) + +*** + +A Library mod and modding api for easier multi-version minecraft and mod loader development + +*** + +### Supported Minecraft Versions + +| Minecraft Version | Support Status | +|-------------------| -------------- | +| < 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. +- 🚧 - Work in Progress; not ready for release. +- ✳️ - Long Term Support; receives changes through backports only. +- ✅ - In Support; the active version, receiving all bugfixes and features directly. + +*** + +## Library Features + +* Universal Config System (TOML Based) +* Built in Helper Classes for Various minecraft features +* Built in Optifine-Compat utilities +* Various utilities for Blockstates, LANG, Math and Rendering +* Cross Mod-Loader Events - Based on [Acara](https://github.com/Keksuccino/acara) +* Cross Mod-Loader Config Screens (Based on [Cloth Config Lite](https://github.com/shedaniel/cloth-config-lite)) +* Automatic ModMenu and Forge Config screen registration +* Built in Cross Mod-Loader Network system +* Nojang Modding API + +*** + +## Setup Instructions + +There's a **wiki coming soon**, but for now, here's some basic instructions for building the project: + +1. `git clone` the project to a safe spot. +2. Install Java's JDK 17. Make sure you have the development version explicitly: + * Fedora: `sudo dnf install java-17-openjdk-devel` + * Ubuntu: `sudo apt install openjdk-17-jdk` + * macOS: `brew install openjdk@17` +3. Set it accordingly: + * Windows/macOS: Set the `JAVA_HOME` environment variable or use system settings + * Linux: `sudo update-alternatives --config java` +4. Navigate to the CraterLib folder, then run a `gradlew` file depending on your operating system: + * Windows: `.\gradlew.bat build` + * macOS/Linux/BSD: `chmod +x gradlew` and `./gradlew` diff --git a/1.21.2/build.gradle b/1.21.2/build.gradle new file mode 100644 index 0000000..04e0bbb --- /dev/null +++ b/1.21.2/build.gradle @@ -0,0 +1,123 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false + id "xyz.wagyourtail.unimined" version "1.3.9" apply false + id "com.hypherionmc.modutils.modpublisher" version "2.1.6" + id "com.hypherionmc.modutils.orion" version "1.0.+" + id 'maven-publish' +} + +var relType = project.properties["releaseType"] ?: "${release_type}" + +orion.setup { + multiProject = true + enableMirrorMaven = true + enableReleasesMaven = true + + dopplerToken = System.getenv("DOPPLER_KEY") + + versioning { + identifier("${relType}") + } +} + +group = project_group + +subprojects { + apply plugin: "xyz.wagyourtail.unimined" + apply plugin: "java" + apply plugin: 'maven-publish' + apply plugin: 'com.github.johnrengelman.shadow' + apply plugin: 'com.hypherionmc.modutils.modpublisher' + + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + + group = rootProject.group + + repositories { + mavenCentral() + mavenLocal() + + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + content { + includeGroup "maven.modrinth" + } + } + } + + configurations { + shade + modCompileOnly + stupidRemapArch + implementation.extendsFrom shade + compileOnly.extendsFrom modCompileOnly + compileOnly.extendsFrom stupidRemapArch + } + + dependencies { + // 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}" + shade "net.kyori:adventure-text-minimessage:${adventure}" + + compileOnly 'net.luckperms:api:5.4' + compileOnly("org.projectlombok:lombok:${lombok}") + annotationProcessor("org.projectlombok:lombok:${lombok}") + } + + jar { + manifest { + attributes([ + 'Specification-Title' : mod_name, + 'Specification-Vendor' : mod_author, + 'Specification-Version' : project.jar.archiveVersion, + 'Implementation-Title' : project.name, + 'Implementation-Version' : project.jar.archiveVersion, + 'Implementation-Vendor' : mod_author, + 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Timestamp' : System.currentTimeMillis(), + 'Built-On-Java' : "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", + 'Built-On-Minecraft' : minecraft_version + ]) + } + } + +/** + * =============================================================================== + * = DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING = + * =============================================================================== + */ + unimined.minecraft(sourceSets.main, true) { + version minecraft_version + + mappings { + mojmap() + devNamespace "mojmap" + } + + mods { + remap(configurations.stupidRemapArch) { + catchAWNamespaceAssertion() + } + } + } + + tasks.withType(JavaCompile).configureEach { + it.options.encoding = 'UTF-8' + it.options.release = 21 + } + + tasks.withType(GenerateModuleMetadata).configureEach { + enabled = false + } +} + +// TODO MODULE JARS \ No newline at end of file diff --git a/1.21.2/gradle.properties b/1.21.2/gradle.properties new file mode 100644 index 0000000..83ddf26 --- /dev/null +++ b/1.21.2/gradle.properties @@ -0,0 +1,48 @@ +#Project +version_major=2 +version_minor=1 +version_patch=1 +version_build=0 + +#Mod +mod_author=HypherionSA +mod_id=craterlib +mod_name=CraterLib + +# Shared +minecraft_version=1.21.2 +project_group=com.hypherionmc.craterlib + +# Fabric +fabric_loader=0.16.7 +fabric_api=0.106.1+1.21.2 + +# Forge +forge_version=50.0.6 + +# NeoForged +neoforge_version=0-beta + +# Dependencies +moon_config=1.0.10 +lombok=1.18.32 +adventure=4.17.0 +rpc_sdk=1.0 +discord_formatter=2.0.0 + +# Mod Dependencies +fabrictailor=2.3.1 +vanish=1.5.4+1.20.5 +mod_menu_version=10.0.0-beta.1 +vanishmod=1.1.12.1 +vanishmod_neo=puxrKAMr +ftb_essentials=2101.1.0 + +# Publishing +curse_id=867099 +modrinth_id=Nn8Wasaq +release_type=release + +# Gradle +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false diff --git a/1.21.2/gradle/wrapper/gradle-wrapper.jar b/1.21.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/1.21.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.21.2/gradle/wrapper/gradle-wrapper.properties b/1.21.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..48c0a02 --- /dev/null +++ b/1.21.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.21.2/gradlew b/1.21.2/gradlew new file mode 100644 index 0000000..b4f908a --- /dev/null +++ b/1.21.2/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env bash + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +ARGV=("$@") +eval set -- $DEFAULT_JVM_OPTS + +IFS=$' +' read -rd '' -a JAVA_OPTS_ARR <<< "$(echo $JAVA_OPTS | xargs -n1)" +IFS=$' +' read -rd '' -a GRADLE_OPTS_ARR <<< "$(echo $GRADLE_OPTS | xargs -n1)" + +exec "$JAVACMD" "$@" "${JAVA_OPTS_ARR[@]}" "${GRADLE_OPTS_ARR[@]}" "-Dorg.gradle.appname=$APP_BASE_NAME" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "${ARGV[@]}" diff --git a/1.21.2/gradlew.bat b/1.21.2/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/1.21.2/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.21.2/settings.gradle b/1.21.2/settings.gradle new file mode 100644 index 0000000..07a31e6 --- /dev/null +++ b/1.21.2/settings.gradle @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { + url "https://mcentral.firstdark.dev/releases" + } + maven { + url "https://maven.firstdark.dev/releases" + } + maven { + url "https://maven.firstdark.dev/snapshots" + } + } +} + +rootProject.name = 'CraterLib' +include("Common", "Fabric", "NeoForge") \ No newline at end of file diff --git a/build.gradle b/build.gradle index 567001e..eb54e2d 100644 --- a/build.gradle +++ b/build.gradle @@ -9,5 +9,5 @@ orionporting { upstreamBranch = "dev" // Enable During Porting patchMode = PatchMode.FUZZY - porting("1.21", "1.20.4", "1.20.2", "1.20", "1.19.3", "1.19.2", "1.18.2") + porting("1.21.2", "1.21", "1.20.4", "1.20.2", "1.20", "1.19.3", "1.19.2", "1.18.2") } \ No newline at end of file diff --git a/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch b/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch new file mode 100644 index 0000000..fcb5831 --- /dev/null +++ b/patches/1.21.2/.jenkins/Jenkinsfile.snapshot.patch @@ -0,0 +1,11 @@ +--- a/.jenkins/Jenkinsfile.snapshot ++++ b/.jenkins/Jenkinsfile.snapshot +@@ -4,7 +4,7 @@ + def majorMc = "1.21.2"; + def modLoaders = "neoforge|fabric|quilt"; + def supportedMc = "1.21.2"; +-def reltype = "port"; ++def reltype = "release"; + + pipeline { + agent { diff --git a/patches/1.21.2/Fabric/src/main/resources/fabric.mod.json.patch b/patches/1.21.2/Fabric/src/main/resources/fabric.mod.json.patch new file mode 100644 index 0000000..e883c10 --- /dev/null +++ b/patches/1.21.2/Fabric/src/main/resources/fabric.mod.json.patch @@ -0,0 +1,11 @@ +--- a/Fabric/src/main/resources/fabric.mod.json ++++ b/Fabric/src/main/resources/fabric.mod.json +@@ -33,7 +33,7 @@ + "depends": { + "fabricloader": ">=0.16.7", + "fabric-api": "*", +- "minecraft": "*", ++ "minecraft": ">=1.21.2", + "java": ">=21" + } + } diff --git a/patches/1.21.2/NeoForge/build.gradle.patch b/patches/1.21.2/NeoForge/build.gradle.patch new file mode 100644 index 0000000..cd02ee7 --- /dev/null +++ b/patches/1.21.2/NeoForge/build.gradle.patch @@ -0,0 +1,16 @@ +--- a/NeoForge/build.gradle ++++ b/NeoForge/build.gradle +@@ -34,12 +34,7 @@ + + unimined.minecraft { + neoForged { +- /*if (isPort) { +- loader "net.neoforged:neoforge:${neoforge_version}:universal" +- } else {*/ +- loader neoforge_version +- //} +- ++ loader neoforge_version + mixinConfig("${mod_id}.mixins.json", "${mod_id}.neoforge.mixins.json") + } + } diff --git a/patches/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml.patch b/patches/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml.patch new file mode 100644 index 0000000..94ea49e --- /dev/null +++ b/patches/1.21.2/NeoForge/src/main/resources/META-INF/neoforge.mods.toml.patch @@ -0,0 +1,18 @@ +--- a/NeoForge/src/main/resources/META-INF/neoforge.mods.toml ++++ b/NeoForge/src/main/resources/META-INF/neoforge.mods.toml +@@ -19,13 +19,13 @@ + [[dependencies.${ mod_id }]] + modId = "neoforge" + type="required" +- versionRange = "[*,)" ++ versionRange = "[21.2.0-beta,)" + ordering = "NONE" + side = "BOTH" + + [[dependencies.${ mod_id }]] + modId = "minecraft" + type="required" +- versionRange = "[*,)" ++ versionRange = "[1.21.2,)" + ordering = "NONE" + side = "BOTH" diff --git a/patches/1.21.2/build.gradle.patch b/patches/1.21.2/build.gradle.patch new file mode 100644 index 0000000..6973612 --- /dev/null +++ b/patches/1.21.2/build.gradle.patch @@ -0,0 +1,18 @@ +--- a/build.gradle ++++ b/build.gradle +@@ -9,15 +9,10 @@ + + var relType = project.properties["releaseType"] ?: "${release_type}" + +-ext { +- isPort = (relType == 'port') +-} +- + orion.setup { + multiProject = true + enableMirrorMaven = true + enableReleasesMaven = true +- //enableSnapshotsMaven = isPort + + dopplerToken = System.getenv("DOPPLER_KEY") + diff --git a/patches/1.21.2/gradle.properties.patch b/patches/1.21.2/gradle.properties.patch new file mode 100644 index 0000000..f12c59e --- /dev/null +++ b/patches/1.21.2/gradle.properties.patch @@ -0,0 +1,11 @@ +--- a/gradle.properties ++++ b/gradle.properties +@@ -41,7 +41,7 @@ + # Publishing + curse_id=867099 + modrinth_id=Nn8Wasaq +-release_type=port ++release_type=release + + # Gradle + org.gradle.jvmargs=-Xmx3G