diff --git a/src/main/java/de/rettichlp/ucutils/UCUtils.java b/src/main/java/de/rettichlp/ucutils/UCUtils.java index d6da2212..53c5cdd1 100644 --- a/src/main/java/de/rettichlp/ucutils/UCUtils.java +++ b/src/main/java/de/rettichlp/ucutils/UCUtils.java @@ -15,7 +15,6 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; import org.slf4j.Logger; @@ -62,7 +61,7 @@ public void onInitialize() { player = client.player; networkHandler = handler; - boolean isUnicaCity = isUnicaCity(); + boolean isUnicaCity = isUnicaCity(handler); storage.setUnicaCity(isUnicaCity); if (isUnicaCity) { client.execute(() -> { @@ -74,19 +73,18 @@ public void onInitialize() { } }); + syncService.checkThirst(); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> this.registry.registerCommands(dispatcher)); ClientLifecycleEvents.CLIENT_STOPPING.register(client -> configuration.saveToFile()); } - private boolean isUnicaCity() { + private boolean isUnicaCity(ClientPlayNetworkHandler networkHandler) { if (getBoolean("fabric.development")) { return true; } - MinecraftClient client = MinecraftClient.getInstance(); - - ClientPlayNetworkHandler networkHandler = client.getNetworkHandler(); if (isNull(networkHandler)) { LOGGER.warn("Not connected to UnicaCity: Network handler is null"); return false; diff --git a/src/main/java/de/rettichlp/ucutils/common/Storage.java b/src/main/java/de/rettichlp/ucutils/common/Storage.java index 3d5a6800..6a991e01 100644 --- a/src/main/java/de/rettichlp/ucutils/common/Storage.java +++ b/src/main/java/de/rettichlp/ucutils/common/Storage.java @@ -107,12 +107,16 @@ public class Storage { @Getter @Setter - private boolean unicaCity = false; + private double thirst = -1.0; @Getter @Setter private ToggledChat toggledChat = NONE; + @Getter + @Setter + private boolean unicaCity = false; + { this.blackMarkets.addAll(stream(BlackMarket.Type.values()) .map(type -> new BlackMarket(type, null, false)) @@ -158,10 +162,12 @@ public void print() { LOGGER.info("minecartEntityToHighlight: {}", this.minecartEntityToHighlight); // moneyAtmAmount LOGGER.info("moneyAtmAmount: {}", this.moneyAtmAmount); - // unicaCity - LOGGER.info("unicaCity: {}", this.unicaCity); + // thirst + LOGGER.info("thirst: {}", this.thirst); // toggledChat LOGGER.info("toggledChat: {}", this.toggledChat); + // unicaCity + LOGGER.info("unicaCity: {}", this.unicaCity); } public Faction getCachedFaction(String playerName) { diff --git a/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java b/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java index 8aca3f8e..bd9a0fde 100644 --- a/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java +++ b/src/main/java/de/rettichlp/ucutils/common/configuration/options/Options.java @@ -36,6 +36,7 @@ public class Options { private ReinforcementType reinforcementType = UNICACITYADDON; private boolean customSounds = true; private AtmInformationType atmInformationType = NONE; + private boolean showThirst = true; @Getter @AllArgsConstructor diff --git a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MainOptionsScreen.java b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MainOptionsScreen.java index 54794b63..7950ea4e 100644 --- a/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MainOptionsScreen.java +++ b/src/main/java/de/rettichlp/ucutils/common/gui/screens/options/MainOptionsScreen.java @@ -21,6 +21,8 @@ public class MainOptionsScreen extends OptionsScreen { private static final Text NOTIFICATION_SOUNDS_NAME = translatable("ucutils.options.notification_sounds.name"); private static final Text NOTIFICATION_SOUNDS_TOOLTIP = translatable("ucutils.options.notification_sounds.tooltip"); private static final Text BANK_INFORMATION_NAME = translatable("ucutils.options.atm_information.name"); + private static final Text THIRST_NAME = translatable("ucutils.options.thirst.name"); + private static final Text THIRST_TOOLTIP = translatable("ucutils.options.thirst.tooltip"); public MainOptionsScreen() { super(new GameMenuScreen(true)); @@ -44,6 +46,9 @@ public void initBody() { renderService.addButton(directionalLayoutWidget3, TEXT_WIDGETS, button -> this.client.setScreen(new WidgetOptionsScreen(this)), 150); renderService.addCyclingButton(directionalLayoutWidget3, BANK_INFORMATION_NAME, Options.AtmInformationType.values(), Options.AtmInformationType::getDisplayName, Options::atmInformationType, Options::atmInformationType, 150); + DirectionalLayoutWidget directionalLayoutWidget4 = directionalLayoutWidget.add(horizontal().spacing(8)); + renderService.addToggleButton(directionalLayoutWidget4, THIRST_NAME, THIRST_TOOLTIP, Options::showThirst, Options::showThirst, 150); + directionalLayoutWidget.forEachChild(this::addDrawableChild); } } diff --git a/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java b/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java index 1c8236a8..88f987c7 100644 --- a/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java +++ b/src/main/java/de/rettichlp/ucutils/common/services/SyncService.java @@ -3,8 +3,19 @@ import de.rettichlp.ucutils.common.models.Faction; import de.rettichlp.ucutils.common.models.FactionEntry; import lombok.Getter; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.Text; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import static de.rettichlp.ucutils.UCUtils.LOGGER; import static de.rettichlp.ucutils.UCUtils.api; @@ -31,6 +42,16 @@ public class SyncService { @Getter private boolean gameSyncProcessActive = false; + private ScheduledExecutorService scheduler; + private ScheduledFuture scheduledTask; + + private boolean executedByTask = false; + private int healthMessageLineCount = 0; + + private static final Pattern HEALTH_STATUS_PATTERN = Pattern.compile( + "=== Zustand von .+ ===|Gesundheit|Blut|Hunger|Durst|Fett|Muskeln" + ); + public void syncFactionMembers() { for (Faction faction : Faction.values()) { if (faction == NULL) { @@ -93,4 +114,85 @@ public void checkForUpdates() { } }); } + + public void checkThirst() { + + ClientReceiveMessageEvents.ALLOW_GAME.register((message, overlay) -> { + + String plainText = message.getString(); + + if (this.executedByTask && HEALTH_STATUS_PATTERN.matcher(plainText).find()) { + this.healthMessageLineCount++; + if (this.healthMessageLineCount >= 7) { + this.executedByTask = false; + this.healthMessageLineCount = 0; + } + } else { + return true; + } + + if (plainText.contains("Durst")) { + for (Text child : message.getSiblings()) { + HoverEvent hoverEvent = child.getStyle().getHoverEvent(); + if (hoverEvent != null && hoverEvent.getAction() == HoverEvent.Action.SHOW_TEXT) { + if (hoverEvent instanceof HoverEvent.ShowText(Text value)) { + String hoverText = value.getString(); + double thirst = this.parseFirstValue(hoverText); + storage.setThirst(thirst); + LOGGER.debug("Set Thirst: {}", thirst); + } + } + } + } + + return false; + }); + + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { + if (!storage.isUnicaCity()) { + return; + } + + LOGGER.info("Starting thirst monitoring scheduler"); + + this.scheduler = Executors.newSingleThreadScheduledExecutor(); + this.scheduledTask = this.scheduler.scheduleAtFixedRate(() -> { + MinecraftClient instance = MinecraftClient.getInstance(); + ClientPlayNetworkHandler networkHandler = instance.getNetworkHandler(); + if (networkHandler == null) { + return; + } + instance.execute(() -> { + this.executedByTask = true; + this.healthMessageLineCount = 0; + networkHandler.sendChatCommand("health"); + }); + }, 0, 10, TimeUnit.SECONDS); + + LOGGER.debug("Thirst monitoring scheduler started"); + }); + + ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> { + + storage.setThirst(-1.0); + + this.executedByTask = false; + this.healthMessageLineCount = 0; + if (this.scheduledTask != null && !this.scheduledTask.isCancelled()) { + this.scheduledTask.cancel(false); + } + if (this.scheduler != null && !this.scheduler.isShutdown()) { + this.scheduler.shutdown(); + } + }); + } + + private double parseFirstValue(String text) { + String cleaned = text.replaceAll("ยง.", ""); + String[] parts = cleaned.split("/"); + if (parts.length > 0) { + return Double.parseDouble(parts[0].trim()); + } + return 0.0; + } } diff --git a/src/main/java/de/rettichlp/ucutils/mixin/InGameHudMixin.java b/src/main/java/de/rettichlp/ucutils/mixin/InGameHudMixin.java new file mode 100644 index 00000000..3cc33aee --- /dev/null +++ b/src/main/java/de/rettichlp/ucutils/mixin/InGameHudMixin.java @@ -0,0 +1,66 @@ +package de.rettichlp.ucutils.mixin; + +import com.llamalad7.mixinextras.sugar.Local; +import de.rettichlp.ucutils.UCUtils; +import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profilers; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(InGameHud.class) +public abstract class InGameHudMixin { + + @Unique + private static final Identifier THIRST_EMPTY_TEXTURE = + Identifier.of("ucutils", "textures/hud/thirst_empty.png"); + @Unique + private static final Identifier THIRST_HALF_TEXTURE = + Identifier.of("ucutils", "textures/hud/thirst_half.png"); + @Unique + private static final Identifier THIRST_FULL_TEXTURE = + Identifier.of("ucutils", "textures/hud/thirst_full.png"); + + @Inject( + method = "renderStatusBars", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHealthBar(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/entity/player/PlayerEntity;IIIIFIIIZ)V", + shift = At.Shift.AFTER + ) + ) + private void inject(DrawContext context, CallbackInfo ci, @Local(ordinal = 3) int m, @Local(ordinal = 8) int r) { + if (!UCUtils.configuration.getOptions().showThirst() || UCUtils.storage.getThirst() == -1) { + return; + } + Profilers.get().swap("thirst"); + this.ucutils$renderThirst(context, r, m); + } + + @Unique + private void ucutils$renderThirst(DrawContext context, int y, int rightX) { + double thirst = UCUtils.storage.getThirst(); + + for (int i = 0; i < 10; ++i) { + int x = rightX - (i * 8) - 10; + + double currentThirst = thirst - (i * 2.0); + + Identifier texture; + if (currentThirst >= 2.0) { + texture = THIRST_FULL_TEXTURE; + } else if (currentThirst >= 1.0) { + texture = THIRST_HALF_TEXTURE; + } else { + texture = THIRST_EMPTY_TEXTURE; + } + + context.drawTexture(RenderPipelines.GUI_TEXTURED, texture, x, y, 0, 0, 9, 9, 9, 9); + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ucutils/lang/de_de.json b/src/main/resources/assets/ucutils/lang/de_de.json index 15e4721e..f716c8c3 100644 --- a/src/main/resources/assets/ucutils/lang/de_de.json +++ b/src/main/resources/assets/ucutils/lang/de_de.json @@ -112,5 +112,7 @@ "ucutils.screen.faction.blacklist.header.reason": "Grund", "ucutils.screen.faction.blacklist.header.price": "Preis", "ucutils.screen.faction.blacklist.header.kills": "Tode", - "ucutils.screen.shutdown_abort.button.name": "Herunterfahren abbrechen" + "ucutils.screen.shutdown_abort.button.name": "Herunterfahren abbrechen", + "ucutils.options.thirst.name": "Zeige Durst", + "ucutils.options.thirst.tooltip": "Zeige deinen Durst in deiner Hotbar an" } diff --git a/src/main/resources/assets/ucutils/lang/en_gb.json b/src/main/resources/assets/ucutils/lang/en_gb.json index b1a4017d..b30990d2 100644 --- a/src/main/resources/assets/ucutils/lang/en_gb.json +++ b/src/main/resources/assets/ucutils/lang/en_gb.json @@ -112,5 +112,7 @@ "ucutils.screen.faction.blacklist.header.reason": "Reason", "ucutils.screen.faction.blacklist.header.price": "Price", "ucutils.screen.faction.blacklist.header.kills": "Kills", - "ucutils.screen.shutdown_abort.button.name": "Abort shutdown" + "ucutils.screen.shutdown_abort.button.name": "Abort shutdown", + "ucutils.options.thirst.name": "Show Thirst", + "ucutils.options.thirst.tooltip": "Display your thirst on your hotbar" } diff --git a/src/main/resources/assets/ucutils/lang/en_us.json b/src/main/resources/assets/ucutils/lang/en_us.json index b1a4017d..b30990d2 100644 --- a/src/main/resources/assets/ucutils/lang/en_us.json +++ b/src/main/resources/assets/ucutils/lang/en_us.json @@ -112,5 +112,7 @@ "ucutils.screen.faction.blacklist.header.reason": "Reason", "ucutils.screen.faction.blacklist.header.price": "Price", "ucutils.screen.faction.blacklist.header.kills": "Kills", - "ucutils.screen.shutdown_abort.button.name": "Abort shutdown" + "ucutils.screen.shutdown_abort.button.name": "Abort shutdown", + "ucutils.options.thirst.name": "Show Thirst", + "ucutils.options.thirst.tooltip": "Display your thirst on your hotbar" } diff --git a/src/main/resources/assets/ucutils/textures/hud/thirst_empty.png b/src/main/resources/assets/ucutils/textures/hud/thirst_empty.png new file mode 100644 index 00000000..226a5af8 Binary files /dev/null and b/src/main/resources/assets/ucutils/textures/hud/thirst_empty.png differ diff --git a/src/main/resources/assets/ucutils/textures/hud/thirst_full.png b/src/main/resources/assets/ucutils/textures/hud/thirst_full.png new file mode 100644 index 00000000..e53baa22 Binary files /dev/null and b/src/main/resources/assets/ucutils/textures/hud/thirst_full.png differ diff --git a/src/main/resources/assets/ucutils/textures/hud/thirst_half.png b/src/main/resources/assets/ucutils/textures/hud/thirst_half.png new file mode 100644 index 00000000..621672de Binary files /dev/null and b/src/main/resources/assets/ucutils/textures/hud/thirst_half.png differ diff --git a/src/main/resources/ucutils.mixins.json b/src/main/resources/ucutils.mixins.json index 46bfca29..fff14ead 100644 --- a/src/main/resources/ucutils.mixins.json +++ b/src/main/resources/ucutils.mixins.json @@ -16,6 +16,7 @@ "ChatScreenMixin", "ClientPlayerEntityMixin", "GameMenuScreenMixin", + "InGameHudMixin", "PlayerEntityRendererMixin", "PlayerListHudMixin" ]