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
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
strategy:
matrix:
game_version: [ # Update this when adding new game versions!
"1.21.11",
"1.21.6",
"1.21.5",
"1.21.4",
Expand Down
6 changes: 4 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import xyz.srnyx.gradlegalaxy.utility.setupJava

plugins {
java
id("fabric-loom") version "1.11-SNAPSHOT"
id("fabric-loom") version "1.14-SNAPSHOT"
id("xyz.srnyx.gradle-galaxy") version "2.0.2"
}

Expand Down Expand Up @@ -37,10 +37,12 @@ dependencies {
if (hasProperty("deps.placeholder_api")) dependencies.modCompileOnly("eu.pb4", "placeholder-api", property("deps.placeholder_api").toString())

// Replacements for fabric.mod.json and config.json
val mixinConfig = if (stonecutter.current.version == "1.21.11") "eventutils-1.21.11.mixin.json" else "eventutils.mixin.json"
addReplacementsTask(setOf("fabric.mod.json"), getDefaultReplacements() + mapOf(
"mod_name" to property("mod.name").toString(),
"mod_version" to property("mod.version").toString(),
"deps_minecraft" to property("deps.minecraft").toString()))
"deps_minecraft" to property("deps.minecraft").toString(),
"mixin_config" to mixinConfig))

base {
archivesName = rootProject.name
Expand Down
80 changes: 80 additions & 0 deletions crop_sheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Crop sheet.png (3 cols x 2 rows) into individual plus-tag icons with transparent background."""
from pathlib import Path

try:
from PIL import Image
except ImportError:
print("Install Pillow: pip install Pillow")
raise

# Order: row0 = bee, white, linked; row1 = booster, contrib, admin
NAMES = ["bee", "white", "linked", "booster", "contrib", "admin"]
ICON_SIZE = 64 # match PlusTagRenderer.TEX_SIZE for sharp in-game scaling
# Pixels within this distance of the sheet background color become transparent (0–255 per channel)
BG_TOLERANCE = 25

SCRIPT_DIR = Path(__file__).resolve().parent
OUT_DIR = SCRIPT_DIR / "src" / "main" / "resources" / "assets" / "eventutils" / "textures" / "gui"
# Prefer sheet in project root, then plus_sheet in gui folder
SHEET_CANDIDATES = [SCRIPT_DIR / "sheet.png", OUT_DIR / "plus_sheet.png"]


def make_bg_transparent(img: Image.Image, bg_rgba: tuple, tolerance: int) -> Image.Image:
"""Replace pixels matching the background color (within tolerance) with transparent."""
data = img.getdata()
r0, g0, b0, a0 = bg_rgba
out = []
for p in data:
if len(p) == 3:
r, g, b = p
if abs(r - r0) <= tolerance and abs(g - g0) <= tolerance and abs(b - b0) <= tolerance:
out.append((0, 0, 0, 0))
else:
out.append((r, g, b, 255))
else:
r, g, b, a = p
if abs(r - r0) <= tolerance and abs(g - g0) <= tolerance and abs(b - b0) <= tolerance:
out.append((0, 0, 0, 0))
else:
out.append((r, g, b, a))
img.putdata(out)
return img


def main():
SHEET = next((p for p in SHEET_CANDIDATES if p.exists()), None)
if SHEET is None:
print(f"Not found: tried {SHEET_CANDIDATES}")
return 1
print(f"Using sheet: {SHEET}")
img = Image.open(SHEET).convert("RGBA")
w, h = img.size
# Use top-left corner as background color
bg_rgba = img.getpixel((0, 0))
if len(bg_rgba) == 3:
bg_rgba = (bg_rgba[0], bg_rgba[1], bg_rgba[2], 255)
print(f"Background color (will be made transparent): {bg_rgba}")

col_w = w // 3
row_h = h // 2
OUT_DIR.mkdir(parents=True, exist_ok=True)
idx = 0
for row in range(2):
for col in range(3):
x = col * col_w
y = row * row_h
cw = (w - x) if col == 2 else col_w
ch = (h - y) if row == 1 else row_h
crop = img.crop((x, y, x + cw, y + ch))
crop = crop.resize((ICON_SIZE, ICON_SIZE), Image.Resampling.LANCZOS)
crop = make_bg_transparent(crop, bg_rgba, BG_TOLERANCE)
out_path = OUT_DIR / f"{NAMES[idx]}.png"
crop.save(out_path)
print(f"Saved {out_path.name} ({ICON_SIZE}x{ICON_SIZE}, transparent bg)")
idx += 1
print("Done.")
return 0

if __name__ == "__main__":
raise SystemExit(main())
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ stonecutter {
centralScript = "build.gradle.kts"
shared {
versions( // Make sure to update .github/workflows/publish.yml when changing versions!
"1.21.11",
"1.21.6",
"1.21.5",
"1.21.4",
Expand Down
Binary file added sheet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/main/java/cc/aabss/eventutils/EventInfoScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ public class EventInfoScreen extends Screen {
@NotNull private final JsonObject json;

public EventInfoScreen(@NotNull JsonObject json) {
//? if >=1.21.11 {
/*super(Text.translatable(EventUtils.MOD.keybindManager.eventInfoKey.getId()));
*///?} else {
super(Text.translatable(EventUtils.MOD.keybindManager.eventInfoKey.getTranslationKey()));
//?}
this.json = json;
}

Expand Down
63 changes: 62 additions & 1 deletion src/main/java/cc/aabss/eventutils/EventUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import cc.aabss.eventutils.websocket.SocketEndpoint;
import cc.aabss.eventutils.websocket.WebSocketClient;
import cc.aabss.eventutils.config.EventConfig;
import cc.aabss.eventutils.config.PlayerGroup;
import cc.aabss.eventutils.plustag.EventAlertsApi;

import com.google.gson.JsonObject;

Expand Down Expand Up @@ -59,7 +61,8 @@ public class EventUtils implements ClientModInitializer {
public KeybindManager keybindManager;
@NotNull public final EventServerManager eventServerManager = new EventServerManager(this);
@NotNull public final Map<EventType, String> lastIps = new EnumMap<>(EventType.class);
public boolean hidePlayers = false;
/** 0 = first group (or hide-all when no groups), 1 = second group, ... ; groups.size() = players revealed */
public int hidePlayersViewMode = 0;

public EventUtils() {
MOD = this;
Expand All @@ -85,6 +88,21 @@ public void onInitializeClient() {
// Update checker
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> updateChecker.checkUpdate());

// Fetch Event Alerts plus tags for local player
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
if (client.player != null) {
var uuid = client.player.getUuid();
LOGGER.info("[EventUtils] JOIN: scheduling Event Alerts fetch for local player uuid={}", uuid);
EventAlertsApi.scheduleFetchIfNeeded(uuid.toString());
} else {
LOGGER.info("[EventUtils] JOIN: client.player is null, skipping fetch (will retry when tab list is opened)");
}
});
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
LOGGER.info("[EventUtils] DISCONNECT: clearing Event Alerts cache");
EventAlertsApi.clearCache();
});

// Initialize keybind manager
keybindManager = new KeybindManager(this);

Expand Down Expand Up @@ -146,6 +164,49 @@ public static boolean isNPC(@NotNull String name) {
return isNPC(name, false);
}

/** Whether the current view mode is "players revealed" (show everyone). */
public boolean isHidePlayersRevealed() {
final int n = config.groups.size();
if (n == 0) return hidePlayersViewMode == 1;
return hidePlayersViewMode >= n;
}

/** Whether we are in a "hide" mode (any group or hide-all). */
public boolean isInHidePlayersMode() {
final int n = config.groups.size();
if (n == 0) return hidePlayersViewMode == 0;
return hidePlayersViewMode < n;
}

/** Current group when in group view mode, or null if revealed or no groups. */
@Nullable
public PlayerGroup getCurrentViewGroup() {
final var groups = config.groups;
if (groups.isEmpty() || hidePlayersViewMode >= groups.size()) return null;
return groups.get(hidePlayersViewMode);
}

/**
* True if the player (by lowercased name) should be visible with current view mode.
* Caller must exclude main player.
*/
public boolean isPlayerVisible(@NotNull String nameLower) {
if (isHidePlayersRevealed()) return true;
if (config.whitelistedPlayers.contains(nameLower) || isNPC(nameLower)) return true;
final PlayerGroup group = getCurrentViewGroup();
if (group == null) return false; // no groups, hide mode: only whitelist/NPC
return group.containsPlayer(nameLower);
}

/** True if the nametag for this visible player should be drawn (per-group setting when in group view). */
public boolean shouldShowNametagFor(@NotNull String nameLower) {
if (!isInHidePlayersMode()) return true;
final PlayerGroup group = getCurrentViewGroup();
if (group == null) return true; // hide-all with no groups: use default
if (!group.containsPlayer(nameLower)) return true; // whitelist/NPC visibility: show nametag
return group.isShowNametags();
}

@Contract(pure = true)
public static int max(int... values) {
int max = Integer.MIN_VALUE;
Expand Down
50 changes: 41 additions & 9 deletions src/main/java/cc/aabss/eventutils/KeybindManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
//? if >=1.21.11 {
/*import net.minecraft.util.Identifier;
*///?}

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -19,6 +22,7 @@
import java.util.HashMap;
import java.util.Map;

import static net.minecraft.text.Text.literal;
import static net.minecraft.text.Text.translatable;


Expand All @@ -31,22 +35,36 @@ public class KeybindManager {

public KeybindManager(@NotNull EventUtils mod) {
// Keybindings
//? if >=1.21.11 {
/*final KeyBinding.Category category = KeyBinding.Category.create(Identifier.of("eventutils", "key.category.eventutils"));
eventInfoKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.eventutils.eventinfo",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_RIGHT_SHIFT,
category));
final KeyBinding hidePlayersKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.eventutils.hideplayers",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_F10,
category));
*///?} else {
eventInfoKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.eventutils.eventinfo",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_RIGHT_SHIFT,
CATEGORY));
final KeyBinding hidePlayersKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.eventutils.hideplayers",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_F10,
CATEGORY));
//?}
// DEV: Uncomment to force test event
// final KeyBindingMixin testEventKey = (KeyBindingMixin) KeyBindingHelper.registerKeyBinding(new KeyBinding(
// "key.eventutils.testevent",
// InputUtil.Type.KEYSYM,
// GLFW.GLFW_KEY_SEMICOLON,
// CATEGORY));
final KeyBinding hidePlayersKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.eventutils.hideplayers",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_F10,
CATEGORY));

ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (windowHandle == null) windowHandle = client.getWindow().getHandle();
Expand Down Expand Up @@ -79,17 +97,31 @@ public KeybindManager(@NotNull EventUtils mod) {
// In-game keybinds
if (client.player == null) return;

// Hide players key
// Hide players key: cycle Group 1 -> Group 2 -> ... -> Players Revealed -> repeat
if (hidePlayersKey.wasPressed()) {
mod.hidePlayers = !mod.hidePlayers;
client.player.sendMessage(translatable(mod.hidePlayers ? "eventutils.hideplayers.enabled" : "eventutils.hideplayers.disabled")
.formatted(mod.hidePlayers ? Formatting.GREEN : Formatting.RED), true);
final int groupCount = mod.config.groups.size();
final int totalStates = groupCount == 0 ? 2 : groupCount + 1;
mod.hidePlayersViewMode = (mod.hidePlayersViewMode + 1) % totalStates;
final boolean revealed = EventUtils.MOD.isHidePlayersRevealed();
final Text message;
if (revealed) {
message = translatable("eventutils.hideplayers.view_revealed").formatted(Formatting.GREEN);
} else {
final var group = EventUtils.MOD.getCurrentViewGroup();
message = (group != null ? literal(group.getName()) : translatable("eventutils.hideplayers.view_whitelist_only"))
.formatted(Formatting.GREEN);
}
client.player.sendMessage(translatable("eventutils.hideplayers.view_prefix").append(message), true);
}
});
}

private boolean canNotPress(@NotNull KeyBinding keyBinding) {
//? if >=1.21.11 {
/*final String translationKey = keyBinding.getId();
*///?} else {
final String translationKey = keyBinding.getTranslationKey();
//?}
final Long lastPressTime = lastKeyPresses.get(translationKey);
final long now = System.currentTimeMillis();
if (lastPressTime != null && now - lastPressTime < 500) return true;
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/cc/aabss/eventutils/commands/CommandRegister.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cc.aabss.eventutils.commands;

import cc.aabss.eventutils.EventType;
import cc.aabss.eventutils.EventUtils;
import cc.aabss.eventutils.config.PlayerGroup;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
Expand Down Expand Up @@ -96,12 +98,26 @@ public static void register(@NotNull CommandDispatcher<FabricClientCommandSource
return 0;
}).build();

final LiteralCommandNode<FabricClientCommandSource> groupMsg = ClientCommandManager
.literal("groupmsg")
.then(ClientCommandManager.argument("group", StringArgumentType.word())
.suggests((context, builder) -> {
for (final PlayerGroup g : EventUtils.MOD.config.groups) builder.suggest(g.getName());
return builder.buildFuture();
})
.then(ClientCommandManager.argument("message", StringArgumentType.greedyString())
.executes(context -> GroupMsgCmd.groupMsg(context,
StringArgumentType.getString(context, "group"),
StringArgumentType.getString(context, "message")))))
.build();

// Build command tree
dispatcher.getRoot().addChild(main);
main.addChild(config);
main.addChild(teleport);
main.addChild(priority);
main.addChild(priorityTop);
main.addChild(countName);
main.addChild(groupMsg);
}
}
Loading
Loading