Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eb78e0d
Updated build system
brunoxkk0 Jul 8, 2026
524f681
Reformatting the code
brunoxkk0 Jul 8, 2026
2aa3022
Refactored all references to `Tags.*` constants by replacing them wit…
brunoxkk0 Jul 8, 2026
10cc8ac
Removed unused imports of `Tags` throughout the codebase.
brunoxkk0 Jul 16, 2026
ee40105
Refactored Mixin and TargetedMod systems; introduced VanillaMixins an…
brunoxkk0 Jul 16, 2026
86b2209
Added compatibility for Angelica mod; implemented custom glyph render…
brunoxkk0 Jul 16, 2026
5e379a0
Refactored `addClientMixins` and `addServerMixins` calls to eliminate…
brunoxkk0 Jul 16, 2026
5e5dbad
Improved glyph rendering in `BatchingFontRendererMixin`: added custom…
brunoxkk0 Jul 16, 2026
c4c63f7
Enhanced glyph rendering in `BatchingFontRendererMixin`: added suppor…
brunoxkk0 Jul 16, 2026
a6cd1cb
Refactored glyph rendering in `BatchingFontRendererMixin`: simplified…
brunoxkk0 Jul 16, 2026
b3e29f7
Adjusted glyph Y-offset in `BatchingFontRendererMixin` to improve ren…
brunoxkk0 Jul 16, 2026
734814a
Refactored glyph batching in `BatchingFontRendererMixin`: introduced …
brunoxkk0 Jul 16, 2026
4cae906
Adjusted glyph Y-offset in `BatchingFontRendererMixin` for improved r…
brunoxkk0 Jul 16, 2026
a5709e9
Added support for modern fonts in `BatchingFontRendererMixin` and `Fo…
brunoxkk0 Jul 16, 2026
4e63353
Added shadow rendering pass for `ModernFontEntry` in `BatchingFontRen…
brunoxkk0 Jul 16, 2026
1edceb6
Expanded `modern_fonts.json` with additional glyph entries and update…
brunoxkk0 Jul 16, 2026
953bf4f
Refactored `ModernFontQuad` batching in `BatchingFontRendererMixin`: …
brunoxkk0 Jul 16, 2026
5d415a1
Replaced direct OpenGL calls with `GLStateManager` in `BatchingFontRe…
brunoxkk0 Jul 16, 2026
95c0b10
Excluded formatting character (§) from width calculation in `Batching…
brunoxkk0 Jul 16, 2026
feeb9f0
Adjusted glyph X and Y offsets in `BatchingFontRendererMixin` for imp…
brunoxkk0 Jul 17, 2026
ea38b9a
Fixed glyph rendering alignment in `BatchingFontRendererMixin` by adj…
brunoxkk0 Jul 17, 2026
c191040
Added `glyph_sizes.bin` to resources for improved glyph size management.
brunoxkk0 Jul 18, 2026
9b8f031
Removed Angelica compatibility layer and deprecated mixin plugins. Re…
brunoxkk0 Jul 18, 2026
65cbe02
Merge remote-tracking branch 'origin/master' into glow_effect
brunoxkk0 Aug 1, 2026
d7521a1
fix(glow): outline invisible living entities
brunoxkk0 Aug 2, 2026
d266a84
fix(glow): cover invisible horses
brunoxkk0 Aug 2, 2026
4407579
feat(glow): implement entity glow effect with outline shaders, config…
brunoxkk0 Aug 2, 2026
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
16 changes: 16 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,19 @@
plugins {
id("com.gtnewhorizons.gtnhconvention")
}

tasks.named<Test>("test") {
useJUnitPlatform()
}

tasks.named<Test>("test") {
useJUnitPlatform()
}

tasks.named<Test>("test") {
useJUnitPlatform()
}

tasks.named<Test>("test") {
useJUnitPlatform()
}
2 changes: 2 additions & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ dependencies {
runtimeOnlyNonPublishable deobfCurse('skinport-234948:3212017')
runtimeOnlyNonPublishable deobfCurse("angelica-988067:8419617")

testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2'
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
modName = NecroTempus
# ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable.
# LOCAL to test local config updates.
gtnh.settings.blowdryerTag = 0.2.2

modName = NecroTempus

# This is a case-sensitive string to identify your mod. Convention is to use lower case.
modId = necrotempus
Expand Down
12 changes: 12 additions & 0 deletions gradle/gradle-daemon-jvm.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/46949723aaa20c7b64d7ecfed7207034/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d6690dfd71c4c91e08577437b5b2beb0/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3cd7045fca9a72cd9bc7d14a385e594c/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/redirect
toolchainVersion=25
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import io.github.cruciblemc.necrotempus.modules.features.bossbar.network.BossBarPacket;
import io.github.cruciblemc.necrotempus.modules.features.bossbar.network.BossBarPacketHandler;
import io.github.cruciblemc.necrotempus.modules.features.core.ClientResetState;
import io.github.cruciblemc.necrotempus.modules.features.glow.network.GlowPacket;
import io.github.cruciblemc.necrotempus.modules.features.glow.network.GlowPacketHandler;
import io.github.cruciblemc.necrotempus.modules.features.packet.NTClientPacket;
import io.github.cruciblemc.necrotempus.modules.features.packet.NTClientPacketHandler;
import io.github.cruciblemc.necrotempus.modules.features.playertab.network.PlayerTabPacket;
Expand Down Expand Up @@ -61,6 +63,7 @@ public class NecroTempus {
DISPATCHER.registerMessage(PlayerTabPacketHandler.class, PlayerTabPacket.class, 2, Side.CLIENT);
DISPATCHER.registerMessage(TitlePacketHandler.class, TitlePacket.class, 3, Side.CLIENT);
DISPATCHER.registerMessage(ActionBarPacketHandler.class, ActionBarPacket.class, 4, Side.CLIENT);
DISPATCHER.registerMessage(GlowPacketHandler.class, GlowPacket.class, 5, Side.CLIENT);
}

@Mod.EventHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.github.cruciblemc.omniconfig.api.annotation.AnnotationConfig;
import io.github.cruciblemc.omniconfig.api.annotation.properties.ConfigBoolean;
import io.github.cruciblemc.omniconfig.api.annotation.properties.ConfigInt;
import io.github.cruciblemc.omniconfig.api.annotation.properties.ConfigString;
import io.github.cruciblemc.omniconfig.api.core.VersioningPolicy;

Expand Down Expand Up @@ -74,4 +75,21 @@ public class NecroTempusConfig {
category = "Angelica")
public static boolean angelicaModernFontsIntegration = true;

@ConfigBoolean(name = "enableEntityGlow", comment = "Enable the 1.9 entity glow outline.", category = "EntityGlow")
public static boolean enableEntityGlow = true;

@ConfigInt(
name = "glowOutlineWidth",
comment = "Outline thickness in pixels (1-4).",
category = "EntityGlow",
min = 1,
max = 4)
public static int glowOutlineWidth = 4;

@ConfigBoolean(
name = "glowDebugKey",
comment = "Dev only: bind a key to glow the entity you look at.",
category = "EntityGlow")
public static boolean glowDebugKey = false;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.cruciblemc.necrotempus.api.glow;

import net.minecraft.entity.player.EntityPlayerMP;

import io.github.cruciblemc.necrotempus.NecroTempus;
import io.github.cruciblemc.necrotempus.modules.features.glow.network.GlowPacket;

/** Server-side entry point: tell a viewer that an entity glows. Mirrors TitleManager/ActionBarManager. */
public class GlowManager {

public static void glow(EntityPlayerMP viewer, int entityId, int rgb, int durationTicks) {
if (viewer != null) {
NecroTempus.DISPATCHER.sendTo(new GlowPacket(GlowPacket.Op.SET, entityId, rgb, durationTicks), viewer);
}
}

public static void unglow(EntityPlayerMP viewer, int entityId) {
if (viewer != null) {
NecroTempus.DISPATCHER.sendTo(new GlowPacket(GlowPacket.Op.REMOVE, entityId, -1, 0), viewer);
}
}

public static void clear(EntityPlayerMP viewer) {
if (viewer != null) {
NecroTempus.DISPATCHER.sendTo(new GlowPacket(GlowPacket.Op.CLEAR, 0, -1, 0), viewer);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.github.cruciblemc.necrotempus.modules.features.compat;

import java.lang.reflect.Field;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* Detects Angelica (GTNH optimization mod) at runtime and forcefully disables
* its batched font renderer via reflection. This allows NecroTempus custom glyphs
* to render correctly, since the vanilla FontRenderer pipeline (renderStringAtPos
* -> renderCharAtPos) is used instead of Angelica's VBO-based batcher.
*/
public class AngelicaCompat {

private static final Logger LOG = LogManager.getLogger("AngelicaCompat");
private static boolean initialized = false;

public static void disableBatchingIfPresent() {
if (initialized) return;
initialized = true;

try {
final Class<?> configClass = Class.forName("com.gtnewhorizons.angelica.config.AngelicaConfig");
final Field field = configClass.getField("enableFontRenderer");

final boolean currentValue = field.getBoolean(null);
if (!currentValue) {
LOG.info("Angelica detected: batched font renderer is already disabled.");
return;
}

field.setBoolean(null, false);
LOG.info("Angelica detected: forcefully disabled batched font renderer for glyph compatibility.");
} catch (ClassNotFoundException e) {
LOG.debug("Angelica not detected, skipping batched font renderer disable.");
} catch (Exception e) {
LOG.error("Failed to disable Angelica batched font renderer: {}", e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.cruciblemc.necrotempus.modules.features.glow;

/** Plain data: the colour and remaining lifetime of one glowing entity. */
public class GlowEntry {

/** 0xRRGGBB, or -1 for "no colour supplied". */
public int rgb;

/** Client ticks left before expiry; < 0 means infinite. */
public int remainingTicks;

public GlowEntry(int rgb, int remainingTicks) {
this.rgb = rgb;
this.remainingTicks = remainingTicks;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.cruciblemc.necrotempus.modules.features.glow;

import java.util.HashMap;
import java.util.Map;

/**
* Minecraft-free registry of glowing entities keyed by entity id. Pure logic so it
* is unit-testable. Entity resolution and colour fallback live in the render layer.
* All access happens on the client thread (mutations scheduled there by the packet
* handler; ticks and reads on the client/render thread).
*/
public class GlowingEntityRegistry {

public static final int INFINITE = -1;

private final Map<Integer, GlowEntry> entries = new HashMap<>();

public void set(int entityId, int rgb, int durationTicks) {
int remaining = durationTicks <= 0 ? INFINITE : durationTicks;
GlowEntry existing = entries.get(entityId);
if (existing != null) {
existing.rgb = rgb;
existing.remainingTicks = remaining;
} else {
entries.put(entityId, new GlowEntry(rgb, remaining));
}
}

public void remove(int entityId) {
entries.remove(entityId);
}

public void clear() {
entries.clear();
}

public boolean isEmpty() {
return entries.isEmpty();
}

public Integer colorFor(int entityId) {
GlowEntry e = entries.get(entityId);
return e == null ? null : e.rgb;
}

public int[] glowingIds() {
int[] ids = new int[entries.size()];
int i = 0;
for (Integer id : entries.keySet()) ids[i++] = id;
return ids;
}

public void tick() {
entries.values()
.removeIf(e -> {
if (e.remainingTicks < 0) return false; // infinite
return --e.remainingTicks <= 0;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.github.cruciblemc.necrotempus.modules.features.glow.client;

import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.Entity;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.MovingObjectPosition;

import org.lwjgl.input.Keyboard;

import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.github.cruciblemc.necrotempus.NecroTempusConfig;
import io.github.cruciblemc.necrotempus.modules.features.glow.GlowingEntityRegistry;

@SideOnly(Side.CLIENT)
public class GlowClientManager {

private static final GlowClientManager INSTANCE = new GlowClientManager();

private final GlowingEntityRegistry registry = new GlowingEntityRegistry();
private KeyBinding debugKey;
private boolean debugKeyWasDown = false;

private GlowClientManager() {}

public static GlowClientManager getInstance() {
return INSTANCE;
}

public GlowingEntityRegistry registry() {
return registry;
}

void registerDebugKey() {
if (NecroTempusConfig.glowDebugKey && debugKey == null) {
debugKey = new KeyBinding("Debug: Glow looked-at entity", Keyboard.KEY_G, "NecroTempus");
ClientRegistry.registerKeyBinding(debugKey);
}
}

@SubscribeEvent
public void onClientTick(TickEvent.ClientTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
registry.tick();
handleDebugKey();
}

@SubscribeEvent
public void onDisconnect(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) {
registry.clear();
}

private void handleDebugKey() {
if (debugKey == null) return;
Minecraft mc = Minecraft.getMinecraft();
if (mc.thePlayer == null || mc.theWorld == null) return;
boolean down = debugKey.getIsKeyPressed();
if (down && !debugKeyWasDown) {
MovingObjectPosition mop = mc.objectMouseOver;
if (mop != null && mop.entityHit != null) {
Entity e = mop.entityHit;
registry.set(e.getEntityId(), 0xFFFFFF, 200);
mc.thePlayer.addChatMessage(new ChatComponentText("Glowing entity " + e.getEntityId()));
}
}
debugKeyWasDown = down;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.cruciblemc.necrotempus.modules.features.glow.client;

import cpw.mods.fml.common.FMLCommonHandler;

public interface GlowRegistry {

static void init() {
GlowClientManager manager = GlowClientManager.getInstance();
manager.registerDebugKey();
FMLCommonHandler.instance()
.bus()
.register(manager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.cruciblemc.necrotempus.modules.features.glow.network;

import cpw.mods.fml.common.network.simpleimpl.IMessage;
import io.netty.buffer.ByteBuf;

/** Server -> client: mark/unmark an entity as glowing. */
public class GlowPacket implements IMessage {

public enum Op {
SET,
REMOVE,
CLEAR
}

private Op op = Op.CLEAR;
private int entityId;
private int rgb = -1;
private int durationTicks;

public GlowPacket() {}

public GlowPacket(Op op, int entityId, int rgb, int durationTicks) {
this.op = op;
this.entityId = entityId;
this.rgb = rgb;
this.durationTicks = durationTicks;
}

@Override
public void fromBytes(ByteBuf buf) {
this.op = Op.values()[buf.readByte()];
this.entityId = buf.readInt();
this.rgb = buf.readInt();
this.durationTicks = buf.readInt();
}

@Override
public void toBytes(ByteBuf buf) {
buf.writeByte(op.ordinal());
buf.writeInt(entityId);
buf.writeInt(rgb);
buf.writeInt(durationTicks);
}

public Op getOp() {
return op;
}

public int getEntityId() {
return entityId;
}

public int getRgb() {
return rgb;
}

public int getDurationTicks() {
return durationTicks;
}
}
Loading
Loading