From d8bc3d4177c71e39aba2e15933485067e50deba3 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 25 Feb 2026 04:31:17 -0500 Subject: [PATCH] CNPC+ Debug Command for Energy Projectiles + Compile Fixes --- .../client/command/CommandCNPCDebugger.java | 42 +++++ .../kamkeel/npcs/command/CommandKamkeel.java | 1 + .../kamkeel/npcs/command/DebugCommand.java | 41 +++++ .../npcs/entity/EntityAbilityBarrier.java | 28 +++ .../npcs/entity/EntityAbilityBeam.java | 54 +++--- .../npcs/entity/EntityAbilityDisc.java | 11 ++ .../npcs/entity/EntityAbilityDome.java | 10 ++ .../npcs/entity/EntityAbilityLaser.java | 11 ++ .../kamkeel/npcs/entity/EntityAbilityOrb.java | 10 ++ .../npcs/entity/EntityAbilityPanel.java | 10 ++ .../npcs/entity/EntityAbilitySweeper.java | 14 ++ .../npcs/entity/EntityEnergyExplosion.java | 12 ++ .../npcs/entity/EntityEnergyProjectile.java | 50 ++++++ .../npcs/entity/EntityEnergySlicer.java | 8 + .../java/kamkeel/npcs/util/CNPCDebug.java | 163 ++++++++++++++++++ .../java/noppes/npcs/client/ClientProxy.java | 5 + 16 files changed, 438 insertions(+), 32 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/client/command/CommandCNPCDebugger.java create mode 100644 src/main/java/kamkeel/npcs/command/DebugCommand.java create mode 100644 src/main/java/kamkeel/npcs/util/CNPCDebug.java diff --git a/src/main/java/kamkeel/npcs/client/command/CommandCNPCDebugger.java b/src/main/java/kamkeel/npcs/client/command/CommandCNPCDebugger.java new file mode 100644 index 000000000..42e18ed6c --- /dev/null +++ b/src/main/java/kamkeel/npcs/client/command/CommandCNPCDebugger.java @@ -0,0 +1,42 @@ +package kamkeel.npcs.client.command; + +import kamkeel.npcs.util.CNPCDebug; +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; + +/** + * Client-only command to toggle debug logging. + * Registered via ClientCommandHandler so it runs entirely on the client + * without sending a packet to the server. + */ +public class CommandCNPCDebugger extends CommandBase { + + @Override + public String getCommandName() { + return "cnpcdebugger"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/cnpcdebugger - Toggle client debug logging for a type (e.g. energy)"; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) { + if (args.length == 0) { + sender.addChatMessage(new ChatComponentText("\u00A7cUsage: /cnpcdebugger ")); + return; + } + + String type = args[0].toLowerCase(); + boolean newState = CNPCDebug.toggleClient(type); + String stateText = newState ? "\u00A7aENABLED" : "\u00A7cDISABLED"; + sender.addChatMessage(new ChatComponentText("\u00A77[CNPC+] \u00A7eClient debug '" + type + "' " + stateText)); + } +} diff --git a/src/main/java/kamkeel/npcs/command/CommandKamkeel.java b/src/main/java/kamkeel/npcs/command/CommandKamkeel.java index 0f0e0dbbf..fb3b75ede 100644 --- a/src/main/java/kamkeel/npcs/command/CommandKamkeel.java +++ b/src/main/java/kamkeel/npcs/command/CommandKamkeel.java @@ -52,6 +52,7 @@ public class CommandKamkeel extends CommandBase { registerCommand(new AuctionCommand()); if (ConfigMain.AttributesEnabled) registerCommand(new AttributeCommand()); + registerCommand(new DebugCommand()); } public void registerCommand(CommandKamkeelBase command) { diff --git a/src/main/java/kamkeel/npcs/command/DebugCommand.java b/src/main/java/kamkeel/npcs/command/DebugCommand.java new file mode 100644 index 000000000..1cb497a93 --- /dev/null +++ b/src/main/java/kamkeel/npcs/command/DebugCommand.java @@ -0,0 +1,41 @@ +package kamkeel.npcs.command; + +import kamkeel.npcs.util.CNPCDebug; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; + +import static kamkeel.npcs.util.ColorUtil.sendResult; + +public class DebugCommand extends CommandKamkeelBase { + + @Override + public String getCommandName() { + return "debug"; + } + + @Override + public String getDescription() { + return "Toggle debug logging types"; + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public boolean runSubCommands() { + return false; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) throws CommandException { + if (args.length == 0) { + throw new CommandException("Usage: /kam debug (e.g. energy)"); + } + + String type = args[0].toLowerCase(); + boolean newState = CNPCDebug.toggleServer(type); + sendResult(sender, "Server debug '" + type + "' " + (newState ? "\u00A7aENABLED" : "\u00A7cDISABLED")); + } +} diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityBarrier.java index 6c1df56d3..3cb3fe021 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityBarrier.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityBarrier.java @@ -2,6 +2,7 @@ package kamkeel.npcs.entity; import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper; import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData; +import kamkeel.npcs.util.CNPCDebug; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -617,4 +618,31 @@ public abstract class EntityAbilityBarrier extends EntityEnergyAbility { this.dataWatcher.updateObject(DW_CHARGING, (byte) (charging ? 1 : 0)); barrierData.readNBT(nbt); } + + // ==================== DEBUG LOGGING ==================== + + /** + * Debug log called every tick for barriers. Subclasses override debugLogBarrierExtra(). + */ + protected void debugLogBarrierTick() { + boolean isClient = worldObj.isRemote; + if (isClient ? !CNPCDebug.isClientEnabled("energy") : !CNPCDebug.isServerEnabled("energy")) + return; + + String className = getClass().getSimpleName(); + String base = String.format("[%s id=%d tick=%d] pos=(%.2f,%.2f,%.2f) charging=%b health=%.1f", + className, getEntityId(), ticksAlive, + posX, posY, posZ, isCharging(), currentHealth); + + String extra = debugLogBarrierExtra(); + String full = extra.isEmpty() ? base : base + " " + extra; + CNPCDebug.log("energy", isClient, full); + } + + /** + * Subclass hook for barrier-specific debug data. + */ + protected String debugLogBarrierExtra() { + return ""; + } } diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java index 33d6e67bc..47e2c7d8d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java @@ -16,7 +16,6 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.Vec3; import net.minecraft.world.World; -import noppes.npcs.LogWriter; import java.util.ArrayList; import java.util.Collections; @@ -61,9 +60,6 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { private int trailFadeTime = 20; // Ticks for trail to fully fade private List trailPointAges = new ArrayList<>(); - // Debug logging - private static final boolean DEBUG_LOGGING = false; - public EntityAbilityBeam(World world) { super(world); } @@ -160,10 +156,6 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { // Add initial trail point at origin (relative 0,0,0) trailPoints.add(Vec3.createVectorHelper(0, 0, 0)); - if (DEBUG_LOGGING && !world.isRemote) { - LogWriter.info("[Beam] Created at origin " + x + ", " + y + ", " + z + " maxDist=" + lifespan.maxDistance); - } - // Calculate initial velocity toward target calculateInitialVelocity(owner, target, x, y, z); } @@ -222,11 +214,6 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { trailPoints.add(Vec3.createVectorHelper(0, 0, 0)); if (isFadingMode()) trailPointAges.add(0); - if (DEBUG_LOGGING && !worldObj.isRemote) { - LogWriter.info("[Beam] startFiring: origin=(" + startX + "," + startY + "," + startZ + - ") headOffset=(" + headOffsetX + "," + headOffsetY + "," + headOffsetZ + - ") motion=(" + motionX + "," + motionY + "," + motionZ + ")"); - } } @Override @@ -237,11 +224,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { headOffsetY * headOffsetY + headOffsetZ * headOffsetZ ); - boolean exceeded = distFromOrigin >= getMaxDistance(); - if (exceeded && !worldObj.isRemote && DEBUG_LOGGING) { - LogWriter.info("[Beam] DEAD: Max distance exceeded. dist=" + distFromOrigin + " max=" + getMaxDistance()); - } - return exceeded; + return distFromOrigin >= getMaxDistance(); } @Override @@ -312,17 +295,6 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { // Add trail point addTrailPoint(); - // Debug log periodically - if (DEBUG_LOGGING && ticksExisted % 20 == 0) { - LogWriter.info("[Beam] Server tick=" + ticksExisted + " headOffset=(" + - String.format("%.2f", headOffsetX) + "," + - String.format("%.2f", headOffsetY) + "," + - String.format("%.2f", headOffsetZ) + ") origin=(" + - String.format("%.2f", startX) + "," + - String.format("%.2f", startY) + "," + - String.format("%.2f", startZ) + ") trail=" + getActiveTrailSize()); - } - // Skip collision checks on first few ticks if (ticksExisted > 2) { checkBlockCollision(headWorldX, headWorldY, headWorldZ); @@ -528,9 +500,27 @@ public class EntityAbilityBeam extends EntityEnergyProjectile { Math.max(prevHeadWorldZ, headZ) + hitSize ); - if (processEntitiesInHitBox(hitBox, headX, headY, headZ) && DEBUG_LOGGING) { - LogWriter.info("[Beam] DEAD: Entity collision at tick " + ticksExisted); - } + processEntitiesInHitBox(hitBox, headX, headY, headZ); + } + + // ==================== DEBUG ==================== + + @Override + protected String debugLogExtra() { + double headWorldX = startX + headOffsetX; + double headWorldY = startY + headOffsetY; + double headWorldZ = startZ + headOffsetZ; + return String.format("headOff=(%.2f,%.2f,%.2f) prevHead=(%.2f,%.2f,%.2f) " + + "origin=(%.2f,%.2f,%.2f) headWorld=(%.2f,%.2f,%.2f) " + + "trail=%d mode=%s anchored=%b beamW=%.2f headSz=%.2f " + + "motion=(%.3f,%.3f,%.3f)", + headOffsetX, headOffsetY, headOffsetZ, + prevHeadOffsetX, prevHeadOffsetY, prevHeadOffsetZ, + startX, startY, startZ, + headWorldX, headWorldY, headWorldZ, + getActiveTrailSize(), + beamMode.name(), isAnchoredMode(), beamWidth, headSize, + motionX, motionY, motionZ); } // ==================== GETTERS FOR RENDERER ==================== diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java index a7ff4ef99..d7c74c3dc 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java @@ -330,6 +330,17 @@ public class EntityAbilityDisc extends EntityEnergyProjectile { updateRotation(); } + // ==================== DEBUG ==================== + + @Override + protected String debugLogExtra() { + return String.format("motion=(%.3f,%.3f,%.3f) radius=%.2f thickness=%.2f " + + "boomerang=%b returning=%b vertical=%b ticksSinceMiss=%d", + motionX, motionY, motionZ, + discRadius, discThickness, + boomerang, returning, vertical, ticksSinceMiss); + } + // ==================== GETTERS ==================== public float getDiscRadius() { diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityDome.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityDome.java index 383f07246..bc00f4332 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityDome.java @@ -122,6 +122,8 @@ public class EntityAbilityDome extends EntityAbilityBarrier { if (worldObj.isRemote && barrierData.meleeEnabled && !isCharging()) { adjustMeleeBoundingBox(); } + + debugLogBarrierTick(); } // ==================== INCOMING CHECK ==================== @@ -548,6 +550,14 @@ public class EntityAbilityDome extends EntityAbilityBarrier { return surfaceDist * surfaceDist; } + // ==================== DEBUG ==================== + + @Override + protected String debugLogBarrierExtra() { + return String.format("radius=%.2f targetRadius=%.2f follow=%b", + domeRadius, targetDomeRadius, followCaster); + } + // ==================== GETTERS ==================== public float getDomeRadius() { diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java index 8bbc8a2fe..3c948d0f0 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java @@ -624,6 +624,17 @@ public class EntityAbilityLaser extends EntityEnergyProjectile { return -1; } + // ==================== DEBUG ==================== + + @Override + protected String debugLogExtra() { + return String.format("dir=(%.3f,%.3f,%.3f) desired=%.2f current=%.2f max=%.2f " + + "fullyExtended=%b end=(%.2f,%.2f,%.2f) width=%.2f expSpd=%.2f origin=(%.2f,%.2f,%.2f)", + dirX, dirY, dirZ, desiredLength, currentLength, maxLength, + fullyExtended, endX, endY, endZ, laserWidth, expansionSpeed, + startX, startY, startZ); + } + // ==================== GETTERS / SETTERS ==================== public float getLaserWidth() { diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java index f7b9cd7e7..07e032829 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java @@ -150,6 +150,16 @@ public class EntityAbilityOrb extends EntityEnergyProjectile { processEntitiesInHitBox(hitBox, nextX, nextY, nextZ); } + // ==================== DEBUG ==================== + + @Override + protected String debugLogExtra() { + return String.format("motion=(%.3f,%.3f,%.3f) homing=%b start=(%.2f,%.2f,%.2f)", + motionX, motionY, motionZ, + homingData != null && homingData.isHoming(), + startX, startY, startZ); + } + // ==================== GETTERS ==================== // Legacy getter for renderer compatibility diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityPanel.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityPanel.java index a9bae51a6..717250fdd 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityPanel.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityPanel.java @@ -143,6 +143,8 @@ public class EntityAbilityPanel extends EntityAbilityBarrier { // Stationary, no update needed break; } + + debugLogBarrierTick(); } private void updateHeld() { @@ -548,6 +550,14 @@ public class EntityAbilityPanel extends EntityAbilityBarrier { return df * df + dr * dr + du * du; } + // ==================== DEBUG ==================== + + @Override + protected String debugLogBarrierExtra() { + return String.format("mode=%s yaw=%.1f w=%.2f h=%.2f", + mode.name(), panelYaw, panelData.panelWidth, panelData.panelHeight); + } + // ==================== GETTERS ==================== public PanelMode getMode() { diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java b/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java index f03324618..69d43da0c 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java @@ -3,6 +3,7 @@ package kamkeel.npcs.entity; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData; +import kamkeel.npcs.util.CNPCDebug; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -168,6 +169,19 @@ public class EntityAbilitySweeper extends EntityEnergyAbility { if (!worldObj.isRemote) { handleDamage(owner); } + + // Debug logging + { + boolean isClient = worldObj.isRemote; + if (isClient ? CNPCDebug.isClientEnabled("energy") : CNPCDebug.isServerEnabled("energy")) { + CNPCDebug.log("energy", isClient, String.format( + "[Sweeper id=%d tick=%d] pos=(%.2f,%.2f,%.2f) angle=%.1f prevAngle=%.1f " + + "speed=%.1f rotations=%d/%d length=%.2f width=%.2f", + getEntityId(), ticksExisted, posX, posY, posZ, + currentAngle, prevAngle, sweepSpeed, + completedRotations, numberOfRotations, beamLength, beamWidth)); + } + } } /** diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java index d90295912..d20975b7d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java @@ -4,6 +4,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData; import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData; +import kamkeel.npcs.util.CNPCDebug; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.MathHelper; import net.minecraft.world.World; @@ -57,6 +58,17 @@ public class EntityEnergyExplosion extends EntityEnergyAbility { float eased = 1.0f - (1.0f - progress) * (1.0f - progress); this.renderRadius = maxRadius * eased; + // Debug logging + { + boolean isClient = worldObj.isRemote; + if (isClient ? CNPCDebug.isClientEnabled("energy") : CNPCDebug.isServerEnabled("energy")) { + CNPCDebug.log("energy", isClient, String.format( + "[Explosion id=%d tick=%d] pos=(%.2f,%.2f,%.2f) renderRadius=%.2f maxRadius=%.2f progress=%.2f", + getEntityId(), ticksExisted, posX, posY, posZ, + renderRadius, maxRadius, getLifeProgress(0f))); + } + } + if (ticksExisted >= durationTicks) { setDead(); } diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java index b33f8d3ec..091af5086 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java @@ -16,6 +16,7 @@ import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData; import kamkeel.npcs.network.packets.data.energy.ProjectileReflectPacket; import kamkeel.npcs.network.packets.data.energyexplosion.EnergyExplosionSpawnPacket; import kamkeel.npcs.util.AnchorPointHelper; +import kamkeel.npcs.util.CNPCDebug; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; @@ -334,6 +335,8 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { if (!previewMode && !worldObj.isRemote) { EventHooks.onEnergyProjectileTick(this); } + + debugLogTick(); } /** @@ -349,6 +352,53 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { if (this.rotationValZ > 360.0f) this.rotationValZ -= 360.0f; } + // ==================== DEBUG LOGGING ==================== + + /** + * Debug log called every tick. Logs position/size/charging state. + * Subclasses override debugLogExtra() for type-specific data. + */ + protected void debugLogTick() { + boolean isClient = worldObj.isRemote; + if (isClient ? !CNPCDebug.isClientEnabled("energy") : !CNPCDebug.isServerEnabled("energy")) + return; + + String className = getClass().getSimpleName(); + boolean dwCharging = isCharging(); // DataWatcher value (what client sees) + boolean localCharging = this.charging; // Local field (server truth) + + String base = String.format("[%s id=%d tick=%d] pos=(%.2f,%.2f,%.2f) prev=(%.2f,%.2f,%.2f) " + + "size=%.3f renderSize=%.3f prevRenderSize=%.3f " + + "charging(dw)=%b charging(local)=%b chargeTick=%d/%d chargeProgress=%.3f", + className, getEntityId(), ticksExisted, + posX, posY, posZ, prevPosX, prevPosY, prevPosZ, + size, renderCurrentSize, prevRenderSize, + dwCharging, localCharging, chargeTick, chargeDuration, getChargeProgress()); + + // Flag mismatch between DataWatcher and local field + if (dwCharging != localCharging) { + base += " !!CHARGE_MISMATCH!!"; + } + + // Client interpolation state (only meaningful on client) + if (isClient) { + base += String.format(" interp(steps=%d target=(%.2f,%.2f,%.2f))", + interpSteps, interpTargetX, interpTargetY, interpTargetZ); + } + + String extra = debugLogExtra(); + String full = extra.isEmpty() ? base : base + " " + extra; + CNPCDebug.log("energy", isClient, full); + } + + /** + * Subclass hook for type-specific debug data. + * Return empty string if nothing extra to log. + */ + protected String debugLogExtra() { + return ""; + } + /** * Check if projectile has exceeded max distance. * diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java b/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java index 96f53f8ca..772f3f704 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java @@ -192,6 +192,14 @@ public class EntityEnergySlicer extends EntityEnergyProjectile { return lastTravelPitch; } + // ==================== DEBUG ==================== + + @Override + protected String debugLogExtra() { + return String.format("motion=(%.3f,%.3f,%.3f) sliceW=%.2f sliceT=%.2f", + motionX, motionY, motionZ, sliceWidth, sliceThickness); + } + // ==================== GETTERS ==================== public float getSliceWidth() { diff --git a/src/main/java/kamkeel/npcs/util/CNPCDebug.java b/src/main/java/kamkeel/npcs/util/CNPCDebug.java new file mode 100644 index 000000000..66cdf3c77 --- /dev/null +++ b/src/main/java/kamkeel/npcs/util/CNPCDebug.java @@ -0,0 +1,163 @@ +package kamkeel.npcs.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.StreamHandler; + +/** + * Debug logging system for CNPC+ entities. + * Writes to logs/cnpc_debug/[type]_[side].log (file only, no console output). + * Toggle at runtime via /cnpcdebugger [type] (client) or /kam debug [type] (server). + */ +public class CNPCDebug { + + private static final String DIR = "logs/cnpc_debug"; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss"); + + // Runtime toggle state per type, separate for client and server + private static final Map serverToggles = new ConcurrentHashMap<>(); + private static final Map clientToggles = new ConcurrentHashMap<>(); + + // Per-type file handlers (lazily initialized) + private static final Map serverHandlers = new ConcurrentHashMap<>(); + private static final Map clientHandlers = new ConcurrentHashMap<>(); + + // ==================== TOGGLE API ==================== + + /** + * Toggle server-side debug for given type. Returns new state. + */ + public static boolean toggleServer(String type) { + type = type.toLowerCase(); + boolean newState = !isServerEnabled(type); + serverToggles.put(type, newState); + if (newState) { + ensureHandler(type, false); + } + return newState; + } + + /** + * Toggle client-side debug for given type. Returns new state. + */ + public static boolean toggleClient(String type) { + type = type.toLowerCase(); + boolean newState = !isClientEnabled(type); + clientToggles.put(type, newState); + if (newState) { + ensureHandler(type, true); + } + return newState; + } + + public static boolean isServerEnabled(String type) { + Boolean val = serverToggles.get(type.toLowerCase()); + return val != null && val; + } + + public static boolean isClientEnabled(String type) { + Boolean val = clientToggles.get(type.toLowerCase()); + return val != null && val; + } + + // ==================== LOGGING API ==================== + + /** + * Log a debug message for the given type on the given side. + * Callers should guard with isServerEnabled/isClientEnabled for performance. + */ + public static void log(String type, boolean client, String msg) { + type = type.toLowerCase(); + Map toggles = client ? clientToggles : serverToggles; + Boolean enabled = toggles.get(type); + if (enabled == null || !enabled) return; + + Handler handler = ensureHandler(type, client); + if (handler == null) return; + + String side = client ? "CLIENT" : "SERVER"; + String timestamp; + synchronized (DATE_FORMAT) { + timestamp = DATE_FORMAT.format(new Date()); + } + String formatted = "[" + timestamp + "][" + side + "][DEBUG-" + type.toUpperCase() + "] " + msg + System.lineSeparator(); + + LogRecord record = new LogRecord(Level.INFO, formatted); + handler.publish(record); + handler.flush(); + } + + /** + * Log on server side. + */ + public static void logServer(String type, String msg) { + log(type, false, msg); + } + + /** + * Log on client side. + */ + public static void logClient(String type, String msg) { + log(type, true, msg); + } + + // ==================== INTERNALS ==================== + + private static Handler ensureHandler(String type, boolean client) { + Map handlers = client ? clientHandlers : serverHandlers; + Handler existing = handlers.get(type); + if (existing != null) return existing; + + synchronized (CNPCDebug.class) { + existing = handlers.get(type); + if (existing != null) return existing; + + try { + File dir = new File(DIR); + if (!dir.exists()) dir.mkdirs(); + + String side = client ? "client" : "server"; + File file = new File(dir, type + "_" + side + ".log"); + + // Rotate old log + if (file.exists()) { + File backup = new File(dir, type + "_" + side + "-prev.log"); + if (backup.exists()) backup.delete(); + file.renameTo(backup); + } + + Handler handler = new StreamHandler(new FileOutputStream(file), new Formatter() { + @Override + public String format(LogRecord record) { + return record.getMessage(); + } + }); + handler.setLevel(Level.ALL); + handlers.put(type, handler); + + // Log header + String timestamp; + synchronized (DATE_FORMAT) { + timestamp = DATE_FORMAT.format(new Date()); + } + LogRecord header = new LogRecord(Level.INFO, + "[" + timestamp + "][" + (client ? "CLIENT" : "SERVER") + "][DEBUG-" + type.toUpperCase() + "] Debug logging started" + System.lineSeparator()); + handler.publish(header); + handler.flush(); + + return handler; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + } +} diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index cd889fa5b..971f3b0fd 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -6,6 +6,7 @@ import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.ObfuscationReflectionHelper; import kamkeel.npcs.addon.client.DBCClient; +import kamkeel.npcs.client.command.CommandCNPCDebugger; import kamkeel.npcs.client.renderer.RenderAbilityBeam; import kamkeel.npcs.client.renderer.RenderAbilityDisc; import kamkeel.npcs.client.renderer.RenderAbilityLaser; @@ -53,6 +54,7 @@ import net.minecraft.util.ChatComponentText; import net.minecraft.util.ReportedException; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; +import net.minecraftforge.client.ClientCommandHandler; import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; @@ -360,6 +362,9 @@ public class ClientProxy extends CommonProxy { ClientTagMapController.Instance = new ClientTagMapController(); MinecraftForge.EVENT_BUS.register(new ClientEventHandler()); + // Debug commands + ClientCommandHandler.instance.registerCommand(new CommandCNPCDebugger()); + // Telegraph rendering system TelegraphManager.initClient(); MinecraftForge.EVENT_BUS.register(new TelegraphRenderer());