mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 05:27:37 +00:00
Merge branch 'dev' into syntax-highlighter-unified
This commit is contained in:
commit
bee84a622c
5 files changed
+157
-65
No files matched your search
@@ -0,0 +1,118 @@
|
||||
package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract base for float comparison-based conditions.
|
||||
* Provides shared CompareType enum, NBT serialization, and FieldDef generation.
|
||||
*/
|
||||
public abstract class ConditionCompare extends AbilityCondition {
|
||||
|
||||
public enum CompareType {
|
||||
ABOVE {
|
||||
@Override
|
||||
public boolean test(float value, float threshold) {
|
||||
return value >= threshold;
|
||||
}
|
||||
},
|
||||
BELOW {
|
||||
@Override
|
||||
public boolean test(float value, float threshold) {
|
||||
return value <= threshold;
|
||||
}
|
||||
},
|
||||
EQUAL {
|
||||
@Override
|
||||
public boolean test(float value, float threshold) {
|
||||
return value == threshold;
|
||||
}
|
||||
};
|
||||
|
||||
public abstract boolean test(float value, float threshold);
|
||||
|
||||
public static CompareType fromOrdinal(int ordinal) {
|
||||
CompareType[] values = values();
|
||||
return (ordinal >= 0 && ordinal < values.length) ? values[ordinal] : ABOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "condition." + name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
protected CompareType compareType = CompareType.ABOVE;
|
||||
|
||||
/**
|
||||
* Returns the value to be compared for this entity (e.g. level, skill level).
|
||||
* Return -1 to indicate the condition should fail immediately.
|
||||
*/
|
||||
protected abstract float getEntityValue(EntityLivingBase entity);
|
||||
|
||||
/**
|
||||
* Returns the threshold that getEntityValue() is compared against.
|
||||
*/
|
||||
protected abstract float getThreshold();
|
||||
|
||||
/**
|
||||
* Appends subclass-specific FieldDefs (e.g. skill picker, level input).
|
||||
* Called before the shared compareType field is added.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
protected abstract void getExtraDefinitions(List<FieldDef> defs);
|
||||
|
||||
/**
|
||||
* Subclasses write their own NBT fields here.
|
||||
*/
|
||||
protected abstract void writeExtraNBT(NBTTagCompound nbt);
|
||||
|
||||
/**
|
||||
* Subclasses read their own NBT fields here.
|
||||
*/
|
||||
protected abstract void readExtraNBT(NBTTagCompound nbt);
|
||||
|
||||
@Override
|
||||
protected boolean checkEntity(EntityLivingBase entity) {
|
||||
float value = getEntityValue(entity);
|
||||
if (value < 0) return false;
|
||||
return compareType.test(value, getThreshold());
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void getConditionDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.enumField("condition.compare_type", CompareType.class, this::getCompareType, this::setCompareType));
|
||||
getExtraDefinitions(defs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setInteger("compareType", compareType.ordinal());
|
||||
writeExtraNBT(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readTypeNBT(NBTTagCompound nbt) {
|
||||
compareType = CompareType.fromOrdinal(nbt.getInteger("compareType"));
|
||||
readExtraNBT(nbt);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GETTERS / SETTERS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
public CompareType getCompareType() {
|
||||
return compareType;
|
||||
}
|
||||
|
||||
public void setCompareType(CompareType compareType) {
|
||||
this.compareType = compareType;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import noppes.npcs.controllers.data.IScriptUnit;
|
||||
import noppes.npcs.items.ItemNpcTool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -32,20 +33,36 @@ public class PacketUtil {
|
||||
// ==================== SCRIPT SESSION TOKEN SYSTEM ====================
|
||||
// Prevents saving script data before the client has received it from the server.
|
||||
// Each GET generates a token sent to the client; each SAVE must echo it back.
|
||||
// Multiple tokens per player are allowed so concurrent script editors don't invalidate each other.
|
||||
// Tokens are consumed on successful verify to prevent unbounded growth.
|
||||
|
||||
private static final Map<UUID, String> scriptSessionTokens = new ConcurrentHashMap<>();
|
||||
private static final int MAX_TOKENS_PER_PLAYER = 10;
|
||||
private static final Map<UUID, Set<String>> scriptSessionTokens = new ConcurrentHashMap<>();
|
||||
|
||||
public static String createScriptSession(EntityPlayerMP player) {
|
||||
String token = Long.toHexString(ThreadLocalRandom.current().nextLong());
|
||||
scriptSessionTokens.put(player.getUniqueID(), token);
|
||||
Set<String> tokens = scriptSessionTokens.computeIfAbsent(player.getUniqueID(), k -> new LinkedHashSet<>());
|
||||
synchronized (tokens) {
|
||||
// Evict oldest tokens if at capacity
|
||||
while (tokens.size() >= MAX_TOKENS_PER_PLAYER) {
|
||||
Iterator<String> it = tokens.iterator();
|
||||
it.next();
|
||||
it.remove();
|
||||
}
|
||||
tokens.add(token);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public static boolean verifyScriptSession(EntityPlayer player, String token) {
|
||||
if (token == null || token.isEmpty())
|
||||
return false;
|
||||
String expected = scriptSessionTokens.get(player.getUniqueID());
|
||||
return expected != null && expected.equals(token);
|
||||
Set<String> tokens = scriptSessionTokens.get(player.getUniqueID());
|
||||
if (tokens == null)
|
||||
return false;
|
||||
synchronized (tokens) {
|
||||
return tokens.remove(token);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearScriptSession(EntityPlayer player) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
import kamkeel.npcs.controllers.ProfileController;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.PacketUtil;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import noppes.npcs.controllers.AuctionController;
|
||||
@@ -78,5 +79,8 @@ public class ServerTickHandler {
|
||||
|
||||
// Save and unload the player's profile data on logout
|
||||
ProfileController.Instance.logout(event.player);
|
||||
|
||||
// Clear script session tokens
|
||||
PacketUtil.clearScriptSession(event.player);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import noppes.npcs.scripted.NpcAPI;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import java.io.PrintWriter;
|
||||
@@ -273,18 +272,20 @@ public class ScriptContainer implements IScriptUnit {
|
||||
unknownFunctions.add(type);
|
||||
}
|
||||
} else {
|
||||
if (instanceBindings != null) {
|
||||
if (!this.cachedFunctions.containsKey(type)) {
|
||||
if (!this.cachedFunctions.containsKey(type)) {
|
||||
ScriptObjectMirror func;
|
||||
if (instanceBindings != null) {
|
||||
Object val = instanceBindings.get(type);
|
||||
ScriptObjectMirror func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null;
|
||||
this.cachedFunctions.put(type, func);
|
||||
func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null;
|
||||
} else {
|
||||
ScriptObjectMirror global = (ScriptObjectMirror) engine.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
func = (ScriptObjectMirror) global.get(type);
|
||||
}
|
||||
ScriptObjectMirror func = this.cachedFunctions.get(type);
|
||||
if (func != null) {
|
||||
func.call(null, event);
|
||||
}
|
||||
} else {
|
||||
((Invocable) engine).invokeFunction(type, event);
|
||||
this.cachedFunctions.put(type, func);
|
||||
}
|
||||
ScriptObjectMirror func = this.cachedFunctions.get(type);
|
||||
if (func != null) {
|
||||
func.call(null, event);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
|
||||
@@ -85,7 +85,6 @@ import noppes.npcs.DataInventory;
|
||||
import noppes.npcs.DataStats;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.IChatMessages;
|
||||
import noppes.npcs.LogWriter;
|
||||
import noppes.npcs.NBTTags;
|
||||
import noppes.npcs.NoppesUtilPlayer;
|
||||
import noppes.npcs.NoppesUtilServer;
|
||||
@@ -483,42 +482,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
abilities.tick();
|
||||
}
|
||||
|
||||
// DEBUG: Log client-side health state every tick to diagnose DI green health spam
|
||||
if (worldObj.isRemote && ticksExisted % 20 == 0) {
|
||||
float h = getHealth();
|
||||
float mh = (float) getMaxHealth();
|
||||
int flags = dataWatcher.getWatchableObjectInt(15);
|
||||
boolean killed = isKilled();
|
||||
LogWriter.info("[CLIENT-HEALTH-DEBUG] NPC=" + display.name
|
||||
+ " id=" + getEntityId()
|
||||
+ " health=" + h
|
||||
+ " maxHealth=" + mh
|
||||
+ " flags=" + Integer.toBinaryString(flags)
|
||||
+ " isKilled=" + killed
|
||||
+ " wasKilled=" + wasKilled
|
||||
+ " isDead=" + isDead
|
||||
+ " deathTime=" + deathTime
|
||||
+ " statsMaxHP=" + stats.maxHealth
|
||||
+ " tick=" + ticksExisted);
|
||||
}
|
||||
|
||||
if (wasKilled != isKilled() && wasKilled) {
|
||||
if (!worldObj.isRemote) {
|
||||
reset();
|
||||
} else {
|
||||
// Client: only reset visual state. Health/flags are synced via DataWatcher.
|
||||
// Full reset() on client can cause health oscillation when maxHealth is desynced.
|
||||
LogWriter.info("[CLIENT-RESET-DEBUG] NPC=" + display.name
|
||||
+ " id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " wasKilled=" + wasKilled
|
||||
+ " isKilled=" + isKilled()
|
||||
+ " tick=" + ticksExisted);
|
||||
deathTime = 0;
|
||||
currentAnimation = EnumAnimation.NONE;
|
||||
updateHitbox();
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
wasKilled = isKilled();
|
||||
@@ -1916,10 +1881,6 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
readSpawnData(ByteBufUtils.readNBT(buf));
|
||||
} catch (IOException e) {
|
||||
if (this.worldObj != null && this.worldObj.isRemote) {
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] ByteBuf readSpawnData FAILED for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " statsMaxHP=" + stats.maxHealth);
|
||||
RequestProperSpawnData.reportMissingData(this);
|
||||
}
|
||||
}
|
||||
@@ -1927,17 +1888,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
|
||||
public void readSpawnData(NBTTagCompound compound) {
|
||||
if (this.worldObj != null && this.worldObj.isRemote) {
|
||||
if(requestClientFix(compound)) {
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] requestClientFix returned true (bad compound) for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " statsMaxHP=" + stats.maxHealth);
|
||||
if(requestClientFix(compound))
|
||||
return;
|
||||
}
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] readSpawnData SUCCESS for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " oldMaxHP=" + (float) getMaxHealth()
|
||||
+ " newMaxHP=" + compound.getDouble("MaxHealth"));
|
||||
}
|
||||
|
||||
stats.maxHealth = compound.getDouble("MaxHealth");
|
||||
|
||||
Reference in new issue
Block a user