Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ dependencies {

modCompileOnly("dev.isxander", "yet-another-config-lib", property("deps.yacl").toString())
modCompileOnly("com.terraformersmc", "modmenu", property("deps.modmenu").toString())

// Discord IPC
include(implementation("com.github.jagrosh:DiscordIPC:master-SNAPSHOT")!!)
include(implementation("org.json:json:20250517")!!)
}

// Add placeholder-api dependency if property exists
Expand Down
135 changes: 135 additions & 0 deletions src/main/java/cc/aabss/eventutils/DiscordRPC.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package cc.aabss.eventutils;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ServerInfo;
import net.minecraft.server.integrated.IntegratedServer;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import com.jagrosh.discordipc.IPCClient;
import com.jagrosh.discordipc.IPCListener;
import com.jagrosh.discordipc.entities.RichPresence;
import com.jagrosh.discordipc.entities.pipe.PipeStatus;
import org.json.JSONObject;


public class DiscordRPC {
private static final OffsetDateTime START = OffsetDateTime.now();

@NotNull private final EventUtils mod;
@Nullable private ScheduledFuture<?> presenceTask;
@Nullable public IPCClient client;

public DiscordRPC(@NotNull EventUtils mod) {
this.mod = mod;
}

public void connect() {
if (!mod.config.discordRpc) return;
if (client != null && client.getStatus() == PipeStatus.CONNECTED) return;

client = new IPCClient(1351016544779374735L);
client.setListener(new IPCListener() {
@Override
public void onReady(IPCClient client) {
EventUtils.LOGGER.info("DISCORD RPC: Connected");
scheduleUpdates();
}

@Override
public void onClose(IPCClient client, JSONObject json) {
EventUtils.LOGGER.info("DISCORD RPC: Disconnected ({})", json);
cancelUpdates();
}

@Override
public void onDisconnect(IPCClient client, Throwable t) {
EventUtils.LOGGER.warn("DISCORD RPC: Disconnected due to error", t);
cancelUpdates();
}
});

try {
client.connect();
} catch (final Exception e) {
EventUtils.LOGGER.error("DISCORD RPC: Failed to connect", e);
client = null;
}
}

public void disconnect() {
cancelUpdates();
if (client != null) {
try {
client.close();
} catch (Exception ignored) {}
}
client = null;
}

private void scheduleUpdates() {
cancelUpdates();
presenceTask = mod.scheduler.scheduleAtFixedRate(this::updatePresence, 0, 10, TimeUnit.SECONDS);
}

private void cancelUpdates() {
if (presenceTask != null) {
presenceTask.cancel(false);
presenceTask = null;
}
}

private void updatePresence() {
if (!mod.config.discordRpc) return;
if (client == null || client.getStatus() != PipeStatus.CONNECTED) return;

final Status status = Status.get();
final String username = MinecraftClient.getInstance().getSession().getUsername();
final RichPresence.Builder builder = new RichPresence.Builder()
.setState("Currently in " + status.text)
.setDetails("Playing as " + username)
.setStartTimestamp(START)
.setLargeImage("logo", "EventUtils" + (Versions.EU_VERSION != null ? " v" + Versions.EU_VERSION : ""))
.setSmallImage("minecraft", "Minecraft" + (Versions.MC_VERSION != null ? " v" + Versions.MC_VERSION : ""));

// These dont work ig
// builder.set("Download the mod!", "https://modrinth.com/mod/alerts");
// builder.setButton2("Join the Discord!", "https://discord.gg/aGDuQcduWZ");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be possible as other applications such as Bloxstrap are able to do it:

Image


try {
client.sendRichPresence(builder.build());
} catch (final Exception e) {
EventUtils.LOGGER.warn("DISCORD RPC: Failed to update presence", e);
}
}

private enum Status {
SINGLEPLAYER("Singleplayer"),
MULTIPLAYER("Multiplayer"),
MAIN_MENU("the Main Menu");

private final String text;

Status(String text) {
this.text = text;
}

@NotNull
private static Status get() {
final MinecraftClient client = MinecraftClient.getInstance();
final IntegratedServer server = client.getServer();
if (server != null && server.isRunning()) return SINGLEPLAYER;
final ServerInfo entry = client.getCurrentServerEntry();
if (entry != null && entry.address != null && !Objects.equals(entry.address, "")) return MULTIPLAYER;
return MAIN_MENU;
}
}
}


9 changes: 8 additions & 1 deletion src/main/java/cc/aabss/eventutils/EventUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class EventUtils implements ClientModInitializer {
@NotNull public final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
@NotNull public final Set<WebSocketClient> webSockets = new HashSet<>();
@NotNull public final UpdateChecker updateChecker = new UpdateChecker(this);
@NotNull public final DiscordRPC discordRPC = new DiscordRPC(this);
@NotNull public final Map<EventType, String> lastIps = new EnumMap<>(EventType.class);
public boolean hidePlayers = false;

Expand All @@ -73,9 +74,13 @@ public void onInitializeClient() {
// Command registration
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> CommandRegister.register(dispatcher));

// Game closed
// Game started / closed
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
if (config.discordRpc) discordRPC.connect();
});
ClientLifecycleEvents.CLIENT_STOPPING.register(client -> {
webSockets.forEach(socket -> socket.close("Game closed"));
discordRPC.disconnect();
});

// Update checker
Expand Down Expand Up @@ -108,6 +113,8 @@ public void onInitializeClient() {
}
if (client.player != null) client.player.sendMessage(Text.literal("No event has happened recently!").formatted(Formatting.RED), true);
}
// Keep Discord presence alive if it was toggled on at runtime
if (config.discordRpc) discordRPC.connect();
});

// Simple queue message
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/cc/aabss/eventutils/config/ConfigScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ public static Screen getConfigScreen(@Nullable Screen parent) {
final YetAnotherConfigLib.Builder builder = YetAnotherConfigLib.createBuilder()
.title(translatable("eventutils.config.title"))
.category(ConfigCategory.createBuilder().name(translatable("eventutils.config.general"))
.option(Option.<Boolean>createBuilder()
.name(translatable("eventutils.config.discord.title"))
.description(OptionDescription.of(translatable("eventutils.config.discord.description")))
.binding(EventConfig.Defaults.DISCORD_RPC, () -> config.discordRpc, newValue -> {
config.discordRpc = newValue;
config.setSave("discord_rpc", config.discordRpc);
if (Boolean.TRUE.equals(newValue)) EventUtils.MOD.discordRPC.connect(); else EventUtils.MOD.discordRPC.disconnect();
})
.controller(ConfigScreen::getBooleanBuilder).build())
.option(Option.<Boolean>createBuilder()
.name(translatable("eventutils.config.teleport.title"))
.description(OptionDescription.of(translatable("eventutils.config.teleport.description")))
Expand Down