From b2178db6955050359a05109b89c78f9cb3d552d4 Mon Sep 17 00:00:00 2001 From: sophie Date: Mon, 28 Oct 2024 00:39:37 +0200 Subject: [PATCH] feat: implement a small part of the mod --- build.gradle | 9 +++ gradle.properties | 3 + src/main/java/sad/ovh/discord/Discord.java | 9 +++ .../sad/ovh/discord/DiscordConfigModel.java | 14 ++++ .../sad/ovh/discord/DiscordRegistration.java | 60 ++++++++++++++++++ .../ovh/discord/MinecraftRegistration.java | 49 ++++++++++++++ src/main/resources/assets/discord/icon.png | Bin 0 -> 9835 bytes 7 files changed, 144 insertions(+) create mode 100644 src/main/java/sad/ovh/discord/DiscordConfigModel.java create mode 100644 src/main/java/sad/ovh/discord/DiscordRegistration.java create mode 100644 src/main/java/sad/ovh/discord/MinecraftRegistration.java create mode 100644 src/main/resources/assets/discord/icon.png diff --git a/build.gradle b/build.gradle index 101d25e..1e8d83c 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,8 @@ repositories { // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. + maven { url 'https://maven.wispforest.io/releases/' } + } dependencies { @@ -36,6 +38,13 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + annotationProcessor modImplementation("io.wispforest:owo-lib:${project.owo_version}") + include "io.wispforest:owo-sentinel:${project.owo_version}" + + implementation("net.dv8tion:JDA:${project.jda_version}") { + exclude module: 'opus-java' + } } processResources { diff --git a/gradle.properties b/gradle.properties index 834a148..c5a62fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,3 +12,6 @@ archives_base_name=discord # Dependencies # check this on https://modmuss50.me/fabric.html fabric_version=0.107.0+1.21.1 + +owo_version=0.12.15+1.21 +jda_version=5.1.2 \ No newline at end of file diff --git a/src/main/java/sad/ovh/discord/Discord.java b/src/main/java/sad/ovh/discord/Discord.java index 9f20655..ec4f395 100644 --- a/src/main/java/sad/ovh/discord/Discord.java +++ b/src/main/java/sad/ovh/discord/Discord.java @@ -1,10 +1,19 @@ package sad.ovh.discord; import net.fabricmc.api.ModInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Discord implements ModInitializer { + public static final String MOD_ID = "slop-discord"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + + public static final sad.ovh.discord.DiscordConfig CONFIG = sad.ovh.discord.DiscordConfig.createAndLoad(); @Override public void onInitialize() { + LOGGER.info("slop's custom discord plugin"); + DiscordRegistration.createClient(); + } } diff --git a/src/main/java/sad/ovh/discord/DiscordConfigModel.java b/src/main/java/sad/ovh/discord/DiscordConfigModel.java new file mode 100644 index 0000000..1c64a5d --- /dev/null +++ b/src/main/java/sad/ovh/discord/DiscordConfigModel.java @@ -0,0 +1,14 @@ +package sad.ovh.discord; + +import io.wispforest.owo.config.Option; +import io.wispforest.owo.config.annotation.Config; +import io.wispforest.owo.config.annotation.Sync; + +@Sync(Option.SyncMode.NONE) +@Config(name = "slop-discord-config", wrapperName = "DiscordConfig") +public class DiscordConfigModel { + public String token = ""; + public String channelId = ""; + public String consoleId = ""; + public String avatarApiUrl = "https://vzge.me/bust/256/%s"; +} diff --git a/src/main/java/sad/ovh/discord/DiscordRegistration.java b/src/main/java/sad/ovh/discord/DiscordRegistration.java new file mode 100644 index 0000000..b863277 --- /dev/null +++ b/src/main/java/sad/ovh/discord/DiscordRegistration.java @@ -0,0 +1,60 @@ +package sad.ovh.discord; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.MessageUpdateEvent; +import net.dv8tion.jda.api.events.session.ReadyEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumSet; + +public class DiscordRegistration { + static public JDA client; + static public TextChannel channel; + static public TextChannel consoleChannel; + + static public Boolean isReady; + + static public void createClient() { + client = JDABuilder + .create(Discord.CONFIG.token(), EnumSet.of(GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT)) + .disableCache(CacheFlag.ACTIVITY, CacheFlag.VOICE_STATE, CacheFlag.EMOJI, CacheFlag.STICKER, CacheFlag.CLIENT_STATUS, CacheFlag.ONLINE_STATUS, CacheFlag.SCHEDULED_EVENTS) + .addEventListeners(new DiscordEvents()) + .build(); + } + + private static class DiscordEvents extends ListenerAdapter { + @Override + public void onReady(@NotNull ReadyEvent event) { + final var self = client.getSelfUser(); + Discord.LOGGER.info("Logged in as {}", self.getAsTag()); + + channel = client.getTextChannelById(Discord.CONFIG.channelId()); + consoleChannel = client.getTextChannelById(Discord.CONFIG.channelId()); + + if (channel == null) { + Discord.LOGGER.error("Channel not found! Set an existing channel ID that I can see!"); + client.shutdown(); + return; + } + if (consoleChannel == null) { + Discord.LOGGER.warn("No console channel was set."); + } + + isReady = true; + } + + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + } + + @Override + public void onMessageUpdate(@NotNull MessageUpdateEvent event) { + } + } +} diff --git a/src/main/java/sad/ovh/discord/MinecraftRegistration.java b/src/main/java/sad/ovh/discord/MinecraftRegistration.java new file mode 100644 index 0000000..89aeaac --- /dev/null +++ b/src/main/java/sad/ovh/discord/MinecraftRegistration.java @@ -0,0 +1,49 @@ +package sad.ovh.discord; + +import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.message.v1.ServerMessageEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.minecraft.server.network.ServerPlayerEntity; + +public class MinecraftRegistration { + public static void register() { + ServerLifecycleEvents.SERVER_STARTING.register(server -> { + if (!DiscordRegistration.isReady) + return; + }); + + ServerLifecycleEvents.SERVER_STARTED.register(server -> { + if (!DiscordRegistration.isReady) + return; + }); + + ServerLifecycleEvents.SERVER_STOPPING.register(server -> { + if (!DiscordRegistration.isReady) + return; + + }); + + ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { + if (!DiscordRegistration.isReady) + return; + }); + + ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> { + if (!DiscordRegistration.isReady) + return; + }); + + ServerLivingEntityEvents.AFTER_DEATH.register((entity, damageSource) -> { + if (!DiscordRegistration.isReady) + return; + if (!(entity instanceof ServerPlayerEntity player)) + return; + }); + + ServerMessageEvents.CHAT_MESSAGE.register((message, sender, params) -> { + if (!DiscordRegistration.isReady) + return; + }); + } +} diff --git a/src/main/resources/assets/discord/icon.png b/src/main/resources/assets/discord/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..baececa904e1590d05dbd1c8aafe8992bbf87ead GIT binary patch literal 9835 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