mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 18:24:10 +00:00
Merge branch 'dev' into pr-278
This commit is contained in:
commit
1532ef722c
90 files changed
+3783
-1261
No files matched your search
@@ -6,6 +6,7 @@ import foxz.commandhelper.annotations.SubCommand;
|
||||
import foxz.commandhelper.permissions.OpOnly;
|
||||
import foxz.commandhelper.permissions.ParamCheck;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import noppes.npcs.NoppesUtilServer;
|
||||
@@ -90,7 +91,7 @@ public class CmdDialog extends ChMcLogger {
|
||||
)
|
||||
public boolean reload(String args[]) {
|
||||
new DialogController().load();
|
||||
SyncController.syncAllDialogs();
|
||||
SyncController.syncAll(SyncType.DIALOG_CATEGORY);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import foxz.commandhelper.annotations.SubCommand;
|
||||
import foxz.commandhelper.permissions.OpOnly;
|
||||
import foxz.commandhelper.permissions.ParamCheck;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.network.packets.data.AchievementPacket;
|
||||
import kamkeel.npcs.network.packets.data.ChatAlertPacket;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -170,7 +171,7 @@ public class CmdQuest extends ChMcLogger {
|
||||
)
|
||||
public boolean reload(String args[]) {
|
||||
new QuestController().load();
|
||||
SyncController.syncAllQuests();
|
||||
SyncController.syncAll(SyncType.QUEST_CATEGORY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package kamkeel.npcs.command;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
@@ -93,7 +94,7 @@ public class DialogCommand extends CommandKamkeelBase {
|
||||
)
|
||||
public void reload(ICommandSender sender, String args[]) {
|
||||
new DialogController().load();
|
||||
SyncController.syncAllDialogs();
|
||||
SyncController.syncAll(SyncType.DIALOG_CATEGORY);
|
||||
sendResult(sender, "Dialogs Reloaded");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package kamkeel.npcs.command;
|
||||
|
||||
import kamkeel.npcs.controllers.ProfileController;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.network.packets.data.AchievementPacket;
|
||||
import kamkeel.npcs.network.packets.data.ChatAlertPacket;
|
||||
import net.minecraft.command.CommandException;
|
||||
@@ -393,7 +394,7 @@ public class QuestCommand extends CommandKamkeelBase {
|
||||
)
|
||||
public void reload(ICommandSender sender, String args[]) {
|
||||
new QuestController().load();
|
||||
SyncController.syncAllQuests();
|
||||
SyncController.syncAll(SyncType.QUEST_CATEGORY);
|
||||
sendResult(sender, "Quests Reloaded");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import kamkeel.npcs.controllers.data.ability.type.AbilitySlam;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityTeleport;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityTrap;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityVortex;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityBeam;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityDisc;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityDome;
|
||||
@@ -44,7 +45,10 @@ import net.minecraftforge.common.util.Constants;
|
||||
import noppes.npcs.controllers.data.AbilityScript;
|
||||
import noppes.npcs.controllers.data.ChainedAbilityScript;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerAbilitySyncHelper;
|
||||
import kamkeel.npcs.entity.EntityEnergyBarrier;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.CustomNpcs;
|
||||
import noppes.npcs.LogWriter;
|
||||
@@ -393,7 +397,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
customAbilitiesById.put(uuid, ability);
|
||||
customAbilityRevision++;
|
||||
LogWriter.script("Saved custom ability: " + name + " [" + uuid + "]");
|
||||
SyncController.syncAllCustomAbilities();
|
||||
SyncController.syncAll(SyncType.CUSTOM_ABILITY);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogWriter.error("Error saving custom ability: " + name, e);
|
||||
@@ -469,16 +473,32 @@ public class AbilityController implements IAbilityHandler {
|
||||
pData.abilityData.lockAbility(name);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// An active toggle holds the resolved Ability directly and keeps ticking it,
|
||||
// so drop it without firing onToggle against an unregistered ability.
|
||||
if (uuid != null && !uuid.isEmpty()) {
|
||||
pData.abilityData.setToggleEntryDirect(uuid, 0);
|
||||
}
|
||||
pData.abilityData.setToggleEntryDirect(name, 0);
|
||||
|
||||
if (pData.hotbarData != null) {
|
||||
pData.hotbarData.validateSlots();
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
pData.save();
|
||||
}
|
||||
|
||||
if (pData.player instanceof EntityPlayerMP) {
|
||||
PlayerAbilitySyncHelper.syncAbilities((EntityPlayerMP) pData.player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customAbilityRevision++;
|
||||
LogWriter.script("Deleted custom ability: " + name);
|
||||
SyncController.syncAllCustomAbilities();
|
||||
SyncController.syncAll(SyncType.CUSTOM_ABILITY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -765,7 +785,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
chainedAbilitiesById.put(uuid, chain);
|
||||
chainedAbilityRevision++;
|
||||
LogWriter.script("Saved chained ability: " + name + " [" + uuid + "]");
|
||||
SyncController.syncAllChainedAbilities();
|
||||
SyncController.syncAll(SyncType.CHAINED_ABILITY);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogWriter.error("Error saving chained ability: " + name, e);
|
||||
@@ -845,7 +865,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
|
||||
chainedAbilityRevision++;
|
||||
LogWriter.script("Deleted chained ability: " + name);
|
||||
SyncController.syncAllChainedAbilities();
|
||||
SyncController.syncAll(SyncType.CHAINED_ABILITY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1362,6 +1382,17 @@ public class AbilityController implements IAbilityHandler {
|
||||
return health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire modifyBarrierMeleeDamage on all extenders. Cumulative — each extender's output feeds the next.
|
||||
*/
|
||||
public float fireModifyBarrierMeleeDamage(EntityEnergyBarrier barrier, EntityLivingBase attacker, float baseDamage) {
|
||||
float damage = baseDamage;
|
||||
for (IAbilityExtender ext : extenders) {
|
||||
damage = ext.modifyBarrierMeleeDamage(barrier, attacker, damage);
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire onAbilityHeal on all extenders. Chain of responsibility — first true wins.
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -840,19 +840,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
return sb.toString();
|
||||
}).tab("General"));
|
||||
|
||||
defs.add(FieldDef.subGuiField("ability.magic.editor",
|
||||
() -> new SubGuiAbilityMagic(magicData.copy()),
|
||||
gui -> {
|
||||
MagicData result = ((SubGuiAbilityMagic) gui).magicData;
|
||||
magicData.getMagics().clear();
|
||||
for (Map.Entry<Integer, MagicEntry> e : result.getMagics().entrySet()) {
|
||||
magicData.getMagics().put(e.getKey(), e.getValue());
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> magicData.isEmpty()
|
||||
? StatCollector.translateToLocal("ability.magic.usesCaster")
|
||||
: magicData.getMagics().size() + " Magic(s)")
|
||||
.tab("General"));
|
||||
defs.add(SubGuiAbilityMagic.createMagicField(magicData));
|
||||
}
|
||||
|
||||
// ── Target tab ───────────────────────────────────────────────
|
||||
@@ -1062,10 +1050,11 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
return null;
|
||||
}
|
||||
|
||||
telegraph.setDurationTicks(windUpTicks);
|
||||
int windUp = getWindUpTicks();
|
||||
telegraph.setDurationTicks(windUp);
|
||||
telegraph.setColor(windUpColor);
|
||||
telegraph.setWarningColor(activeColor);
|
||||
telegraph.setWarningStartTick(Math.max(5, windUpTicks / 4));
|
||||
telegraph.setWarningStartTick(Math.max(5, windUp / 4));
|
||||
telegraph.setHeightOffset(telegraphHeightOffset);
|
||||
|
||||
TelegraphInstance instance = new TelegraphInstance(telegraph, x, y, z, yaw);
|
||||
@@ -1183,7 +1172,9 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
this.burstIndex = 0;
|
||||
this.burstEntities.clear();
|
||||
|
||||
if (windUpTicks <= 0) {
|
||||
// Use the effective windup, not the raw field: when the windup is synced to an
|
||||
// animation the raw field is not the duration that actually applies.
|
||||
if (getWindUpTicks() <= 0) {
|
||||
this.phase = AbilityPhase.ACTIVE;
|
||||
} else {
|
||||
this.phase = AbilityPhase.WINDUP;
|
||||
@@ -1205,7 +1196,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
|
||||
switch (phase) {
|
||||
case WINDUP:
|
||||
if (currentTick >= windUpTicks) {
|
||||
if (currentTick >= getWindUpTicks()) {
|
||||
phase = AbilityPhase.ACTIVE;
|
||||
currentTick = 0;
|
||||
return true;
|
||||
|
||||
+7
-19
@@ -34,27 +34,15 @@ public class ConditionHasEffect extends AbilityCondition {
|
||||
return CustomEffectController.getInstance().hasEffect(player, effectId, effectIndex);
|
||||
}
|
||||
|
||||
public void setEffect(int effectId, int effectIndex) {
|
||||
this.effectId = effectId;
|
||||
this.effectIndex = effectIndex;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void getConditionDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.subGuiField("condition.select_effect",
|
||||
() -> new SubGuiCustomEffectSelect(effectId, effectIndex),
|
||||
gui -> {
|
||||
SubGuiCustomEffectSelect sel = (SubGuiCustomEffectSelect) gui;
|
||||
if (sel.getSelectedEffectId() >= 0) {
|
||||
effectId = sel.getSelectedEffectId();
|
||||
effectIndex = sel.getSelectedIndex();
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> {
|
||||
if (effectId < 0) return "None";
|
||||
CustomEffect effect = getEffect();
|
||||
return effect != null ? effect.getName() : "ID:" + effectId;
|
||||
})
|
||||
.clearable(() -> {
|
||||
effectId = -1;
|
||||
effectIndex = 0;
|
||||
}));
|
||||
defs.add(SubGuiCustomEffectSelect.createSelectField(this));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -74,7 +62,7 @@ public class ConditionHasEffect extends AbilityCondition {
|
||||
return effectId >= 0;
|
||||
}
|
||||
|
||||
private CustomEffect getEffect() {
|
||||
public CustomEffect getEffect() {
|
||||
HashMap<Integer, CustomEffect> map = CustomEffectController.getInstance().getEffectMap(effectIndex);
|
||||
return map != null ? map.get(effectId) : null;
|
||||
}
|
||||
|
||||
+1
-14
@@ -38,20 +38,7 @@ public class ConditionQuestCompleted extends AbilityCondition {
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void getConditionDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.subGuiField("condition.select_quest",
|
||||
() -> new GuiQuestSelection(questId),
|
||||
gui -> {
|
||||
GuiQuestSelection sel = (GuiQuestSelection) gui;
|
||||
if (sel.selectedQuest != null) {
|
||||
questId = sel.selectedQuest.id;
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> {
|
||||
if (questId < 0) return "None";
|
||||
Quest q = QuestController.Instance.quests.get(questId);
|
||||
return q != null ? q.title : "ID:" + questId;
|
||||
})
|
||||
.clearable(() -> questId = -1));
|
||||
defs.add(GuiQuestSelection.createConditionField(this));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
|
||||
@@ -3,6 +3,7 @@ package kamkeel.npcs.controllers.data.ability.extender;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.entity.EntityEnergyBarrier;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
/**
|
||||
@@ -108,6 +109,21 @@ public interface IAbilityExtender {
|
||||
return baseHealth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a barrier entity takes a melee hit, allowing extenders to scale the damage.
|
||||
* A barrier is a plain Entity, so no LivingHurtEvent fires for it and an extender has no
|
||||
* other opportunity to scale melee against it the way it scales {@link #modifyBarrierHealth}.
|
||||
* Cumulative: all extenders apply in sequence, each receiving the previous output.
|
||||
*
|
||||
* @param barrier The barrier being hit
|
||||
* @param attacker The entity dealing the hit
|
||||
* @param baseDamage The damage after the barrier's own melee multiplier
|
||||
* @return The modified damage value
|
||||
*/
|
||||
default float modifyBarrierMeleeDamage(EntityEnergyBarrier barrier, EntityLivingBase attacker, float baseDamage) {
|
||||
return baseDamage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an ability is about to heal an entity.
|
||||
* Return true to handle the healing (skip default entity.heal()).
|
||||
|
||||
@@ -43,7 +43,7 @@ public class AbilityCharge extends AbilityMovement implements IAbilityCharge {
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 20.0f;
|
||||
this.minRange = 4.0f;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 20;
|
||||
// LINE telegraph showing charge path
|
||||
@@ -58,6 +58,11 @@ public class AbilityCharge extends AbilityMovement implements IAbilityCharge {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbilityMovement() {
|
||||
return true; // This ability drives the NPC's motion itself
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetingModeLocked() {
|
||||
return true;
|
||||
|
||||
@@ -3,14 +3,21 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.addon.DBCAddon;
|
||||
import noppes.npcs.AbstractDataAbilities;
|
||||
import noppes.npcs.controllers.AnimationController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import noppes.npcs.controllers.data.Animation;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
import noppes.npcs.api.ability.type.IAbilityCounter;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.entity.EntityNPCInterface;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -25,6 +32,11 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
|
||||
private int counterAnimationId = -1;
|
||||
private String counterAnimationName = "Ability_Guard_Counter";
|
||||
|
||||
// True only while this counter is synchronously dispatching its retaliation damage.
|
||||
// Another counter that is hit by that retaliation inspects this flag on the source
|
||||
// entity's active counter to avoid reacting and creating infinite mutual retaliation.
|
||||
private transient boolean retaliating;
|
||||
|
||||
public AbilityCounter() {
|
||||
this.typeId = "ability.cnpc.counter";
|
||||
this.name = "Counter";
|
||||
@@ -60,6 +72,35 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
|
||||
// DEFEND HOOKS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
protected boolean isValidDamageSource(DamageSource source) {
|
||||
if (!super.isValidDamageSource(source)) {
|
||||
return false;
|
||||
}
|
||||
// Break counter-vs-counter recursion: if the hit is itself a retaliation
|
||||
// from another counter that is mid-dispatch, don't counter it back.
|
||||
Entity sourceEntity = source.getEntity();
|
||||
AbstractDataAbilities abilities = getAbilitiesOf(sourceEntity);
|
||||
if (abilities != null) {
|
||||
Ability current = abilities.getCurrentAbility();
|
||||
if (current instanceof AbilityCounter && ((AbilityCounter) current).retaliating) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static AbstractDataAbilities getAbilitiesOf(Entity entity) {
|
||||
if (entity instanceof EntityPlayer) {
|
||||
PlayerData pData = PlayerData.get((EntityPlayer) entity);
|
||||
return pData != null ? pData.abilityData : null;
|
||||
}
|
||||
if (entity instanceof EntityNPCInterface) {
|
||||
return ((EntityNPCInterface) entity).abilities;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float performDefend(EntityLivingBase attacker, float amount) {
|
||||
// For PERCENT mode with DBC, use the full DBC-calculated damage, not vanilla base
|
||||
@@ -78,13 +119,24 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
|
||||
|
||||
// Counter-attack through the standard ability damage pipeline.
|
||||
// DBCAbilityExtender handles DBC stat routing (ignore flags, scaling, etc.) automatically.
|
||||
if (caster != null && attacker.isEntityAlive() && damage > 0) {
|
||||
applyAbilityDamage(caster, attacker, damage, 0);
|
||||
if (caster != null && attacker != null && attacker.isEntityAlive() && damage > 0) {
|
||||
retaliating = true;
|
||||
try {
|
||||
applyAbilityDamage(caster, attacker, damage, 0);
|
||||
} finally {
|
||||
retaliating = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
retaliating = false;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// OVERRIDES
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -74,6 +74,12 @@ public abstract class AbilityDefend extends Ability implements IAbilityDefend {
|
||||
hitCount = 0;
|
||||
lastAttacker = null;
|
||||
lastDamageTaken = 0.0f;
|
||||
// NPCs reuse one ability instance, so every transient field has to be cleared here.
|
||||
// A leftover pendingCompletion would end the next activation on its first tick.
|
||||
pendingDefendAnimation = null;
|
||||
defendAnimEndTick = -1;
|
||||
lastDefendTick = -1;
|
||||
pendingCompletion = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -107,7 +107,7 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard {
|
||||
readZoneNBT(nbt, 300);
|
||||
this.radius = nbt.getFloat("radius");
|
||||
this.damagePerSecond = nbt.getFloat("damagePerSecond");
|
||||
this.damageInterval = nbt.getInteger("damageInterval");
|
||||
setDamageInterval(nbt.getInteger("damageInterval"));
|
||||
this.affectsCaster = nbt.getBoolean("affectsCaster");
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard {
|
||||
}
|
||||
|
||||
public void setDamageInterval(int damageInterval) {
|
||||
this.damageInterval = damageInterval;
|
||||
this.damageInterval = Math.max(EntityAbilityZone.MIN_DAMAGE_INTERVAL, damageInterval);
|
||||
}
|
||||
|
||||
public boolean isAffectsCaster() {
|
||||
@@ -171,6 +171,7 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard {
|
||||
FieldDef.row(
|
||||
FieldDef.floatField("gui.dps", this::getDamagePerSecond, this::setDamagePerSecond),
|
||||
FieldDef.intField("gui.interval", this::getDamageInterval, this::setDamageInterval)
|
||||
.range(EntityAbilityZone.MIN_DAMAGE_INTERVAL, 2000)
|
||||
),
|
||||
FieldDef.boolField("ability.affectsCaster", this::isAffectsCaster, this::setAffectsCaster)
|
||||
.hover("ability.hover.affectsCaster"),
|
||||
|
||||
@@ -97,6 +97,15 @@ public class AbilityProjectile extends Ability implements IAbilityProjectile {
|
||||
dz /= len;
|
||||
}
|
||||
|
||||
// A rotation-locked NPC cannot turn to follow the target, so it can only hit what its
|
||||
// frozen heading still covers - the same cone the player hit scan uses.
|
||||
if (isRotationLockedForCurrentPhase()) {
|
||||
Vec3 look = caster.getLookVec();
|
||||
if (look != null && (dx * look.xCoord + dy * look.yCoord + dz * look.zCoord) < 0.95) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
applyAbilityDamageWithDirection(caster, target, damage, knockback, dx, dz);
|
||||
world.playSoundAtEntity(caster, "random.bow", 1.0f, 0.8f);
|
||||
|
||||
|
||||
@@ -300,14 +300,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
protected void addPresetFieldDef(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.subGuiField("gui.applyPreset",
|
||||
SubGuiTrapPresetSelector::new,
|
||||
gui -> {
|
||||
SubGuiTrapPresetSelector selector = (SubGuiTrapPresetSelector) gui;
|
||||
if (selector.selectedPreset != null) {
|
||||
applyTrapPresetDefaults(selector.selectedPreset);
|
||||
}
|
||||
}));
|
||||
defs.add(SubGuiTrapPresetSelector.createPresetField(this::applyTrapPresetDefaults));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
|
||||
@@ -690,14 +690,7 @@ public abstract class AbilityZone extends Ability {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
protected void addPresetFieldDef(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.subGuiField("gui.applyPreset",
|
||||
SubGuiZonePresetSelector::new,
|
||||
gui -> {
|
||||
SubGuiZonePresetSelector selector = (SubGuiZonePresetSelector) gui;
|
||||
if (selector.selectedPreset != null) {
|
||||
applyPresetDefaults(selector.selectedPreset);
|
||||
}
|
||||
}));
|
||||
defs.add(SubGuiZonePresetSelector.createPresetField(this::applyPresetDefaults));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
|
||||
+6
-1
@@ -248,7 +248,12 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
* NPCs use their resolved target so launch math can track the target immediately.
|
||||
*/
|
||||
protected EntityLivingBase getLaunchTarget(EntityLivingBase caster, EntityLivingBase target) {
|
||||
return isPlayerCaster(caster) ? null : target;
|
||||
if (isPlayerCaster(caster)) return null;
|
||||
// A rotation-locked NPC cannot turn, so launching from the target's position would let
|
||||
// it fire a perfectly aimed shot regardless of where it is facing. Fall back to the
|
||||
// look vector, which reflects the frozen heading.
|
||||
if (isRotationLockedForCurrentPhase()) return null;
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.profile;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerDataSyncHandler;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -30,7 +30,7 @@ public class CNPCData implements IProfileData {
|
||||
public void save(EntityPlayer player) {
|
||||
PlayerData customNPCData = PlayerData.get(player);
|
||||
customNPCData.save();
|
||||
SyncController.syncPlayerData((EntityPlayerMP) player, false);
|
||||
PlayerDataSyncHandler.syncPlayerData((EntityPlayerMP) player, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package kamkeel.npcs.controllers.sync;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Contract for a synchronized data type. Implementors handle both
|
||||
* server-side serialization and client-side deserialization for their
|
||||
* specific data type.
|
||||
*
|
||||
* <p>Each handler is registered with {@link SyncRegistry} and covers
|
||||
* all three sync actions: RELOAD (full), UPDATE (single entity),
|
||||
* and REMOVE (delete entity).</p>
|
||||
*
|
||||
* <p>Handlers that don't support UPDATE or REMOVE should use the
|
||||
* default no-op implementations.</p>
|
||||
*/
|
||||
public interface SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
/**
|
||||
* Serialize ALL objects of this type into an NBTTagCompound.
|
||||
* This is the supplier for the cache system.
|
||||
*
|
||||
* <p>Called lazily when the cache is dirty and a player needs data.
|
||||
* The returned compound is cached and chunked for transmission.</p>
|
||||
*
|
||||
* @return the serialized compound, or null if this type has no independent cache
|
||||
*/
|
||||
NBTTagCompound serializeAll();
|
||||
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
/**
|
||||
* Handle a RELOAD action on the client. Deserialize the full
|
||||
* dataset and replace the client-side data.
|
||||
*
|
||||
* <p>Implementations should:
|
||||
* <ol>
|
||||
* <li>Deserialize from the compound into a sync buffer</li>
|
||||
* <li>Atomically swap the sync buffer to the primary map</li>
|
||||
* <li>Reset the sync buffer for the next reload</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param fullCompound the full serialized dataset from {@link #serializeAll()}
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleReload(NBTTagCompound fullCompound){
|
||||
// No-op: type does not support individual updates
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an UPDATE action on the client.
|
||||
* Deserialize a single object and insert/replace it in the client-side data.
|
||||
*
|
||||
* <p>Default: no-op. Types that don't support individual updates
|
||||
* use {@code syncAll()} for full reload instead.</p>
|
||||
*
|
||||
* @param compound the serialized single object
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleUpdate(NBTTagCompound compound) {
|
||||
// No-op: type does not support individual updates
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an UPDATE action on the client.
|
||||
* Deserialize a single object and insert/replace it in the client-side data.
|
||||
*
|
||||
* <p>Default: no-op. Types that don't support individual updates
|
||||
* use {@code syncAll()} for full reload instead.</p>
|
||||
*
|
||||
* @param compound the serialized single object
|
||||
* @param categoryKey the parent category key (null if not applicable)
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleUpdate(NBTTagCompound compound, String categoryKey) {
|
||||
// No-op: type does not support individual updates
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle an UPDATE action on the client. Deserialize a single
|
||||
* entity and insert/replace it in the client-side data.
|
||||
*
|
||||
* <p>Default: no-op. Types that don't support individual updates
|
||||
* use {@code syncAll()} for full reload instead.</p>
|
||||
*
|
||||
* @param compound the serialized single entity
|
||||
* @param categoryId the parent category ID (-1 if not applicable)
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
// No-op: type does not support individual updates
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a single object from client-side data.
|
||||
* Full object NBTTagCompound sent finer query control
|
||||
* Int-keyed handlers should override {@link #clientHandleRemove(int)} instead.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleRemove(String key) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a single object with the given ID from client-side data.
|
||||
*
|
||||
* @param id the ID of the object to remove
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
default void clientHandleRemove(int id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this handler supports the UPDATE action.
|
||||
* Used for validation and self-documentation.
|
||||
*/
|
||||
default boolean supportsUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this handler supports the REMOVE action.
|
||||
* Used for validation and self-documentation.
|
||||
*/
|
||||
default boolean supportsRemove() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this type should be included in login sync.
|
||||
* Types that return true will be sent to players on login
|
||||
* and checked for revision deltas on reconnect.
|
||||
*
|
||||
* <p>Default: true. Override to false for types like PLAYERDATA
|
||||
* that are synced through separate mechanisms, or child types
|
||||
* (DIALOG, QUEST) that share their parent's cache.</p>
|
||||
*/
|
||||
default boolean isCachedType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of cache types that must be invalidated when
|
||||
* this type changes. Used for cascade invalidation.
|
||||
*
|
||||
* <p>Most types return just themselves. Parent-child types
|
||||
* (e.g. DIALOG maps to DIALOG_CATEGORY) point children
|
||||
* to the parent's cache.</p>
|
||||
*
|
||||
* @param self the SyncType this handler is registered under
|
||||
* @return the set of types to invalidate
|
||||
*/
|
||||
default Set<SyncType> getInvalidationTargets(SyncType self) {
|
||||
Set<SyncType> set = new HashSet<>();
|
||||
set.add(self);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package kamkeel.npcs.controllers.sync;
|
||||
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import noppes.npcs.LogWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SyncRegistry {
|
||||
|
||||
private static final LinkedHashMap<SyncType, SyncHandler> handlers = new LinkedHashMap<>();
|
||||
|
||||
private static SyncType[] loginTypesCache = null;
|
||||
|
||||
private SyncRegistry() {}
|
||||
|
||||
public static void register(SyncType type, SyncHandler handler) {
|
||||
if (type == null) throw new IllegalArgumentException("SyncType must not be null");
|
||||
if (handler == null) throw new IllegalArgumentException("SyncHandler must not be null for " + type);
|
||||
if (handlers.containsKey(type)) throw new IllegalStateException("SyncHandler already registered for " + type);
|
||||
|
||||
handlers.put(type, handler);
|
||||
loginTypesCache = null;
|
||||
}
|
||||
|
||||
public static SyncHandler getHandler(SyncType type) {
|
||||
return handlers.get(type);
|
||||
}
|
||||
|
||||
public static SyncType[] getLoginTypes() {
|
||||
if (loginTypesCache == null) {
|
||||
List<SyncType> list = new ArrayList<>();
|
||||
for (Map.Entry<SyncType, SyncHandler> e : handlers.entrySet())
|
||||
if (e.getValue().isCachedType())
|
||||
list.add(e.getKey());
|
||||
|
||||
loginTypesCache = list.toArray(new SyncType[0]);
|
||||
}
|
||||
return loginTypesCache;
|
||||
}
|
||||
|
||||
public static Set<SyncType> getInvalidationTargets(SyncType type) {
|
||||
SyncHandler handler = handlers.get(type);
|
||||
if (handler == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return handler.getInvalidationTargets(type);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
handlers.clear();
|
||||
loginTypesCache = null;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isRegistered(SyncType type) {
|
||||
return handlers.containsKey(type);
|
||||
}
|
||||
|
||||
public static Collection<SyncType> getRegisteredTypes() {
|
||||
return Collections.unmodifiableCollection(handlers.keySet());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.RecipeController;
|
||||
import noppes.npcs.controllers.data.RecipeAnvil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#ANVIL_RECIPES}.
|
||||
* RELOAD-only family — does not support individual UPDATE or REMOVE.
|
||||
*/
|
||||
public class AnvilRecipeSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
RecipeController controller = RecipeController.Instance;
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (RecipeAnvil recipe : controller.anvilRecipes.values()) {
|
||||
list.appendTag(recipe.writeNBT(false));
|
||||
}
|
||||
compound.setTag("recipes", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("recipes", 10);
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
RecipeAnvil recipe = new RecipeAnvil();
|
||||
recipe.readNBT(list.getCompoundTagAt(i));
|
||||
RecipeController.syncAnvilRecipes.put(recipe.id, recipe);
|
||||
}
|
||||
|
||||
RecipeController.Instance.anvilRecipes = RecipeController.syncAnvilRecipes;
|
||||
RecipeController.syncAnvilRecipes = new HashMap<>();
|
||||
}
|
||||
|
||||
// RELOAD-only: supportsUpdate() and supportsRemove() remain false (default)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.RecipeController;
|
||||
import noppes.npcs.controllers.data.RecipeCarpentry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#CARPENTRY_RECIPES}.
|
||||
* RELOAD-only family — does not support individual UPDATE or REMOVE.
|
||||
*/
|
||||
public class CarpentryRecipeSyncHandler implements SyncHandler {
|
||||
|
||||
private static final CarpentryRecipeSyncHandler INSTANCE = new CarpentryRecipeSyncHandler();
|
||||
|
||||
public static CarpentryRecipeSyncHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
RecipeController controller = RecipeController.Instance;
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (RecipeCarpentry recipe : controller.carpentryRecipes.values()) {
|
||||
list.appendTag(recipe.writeNBT(false));
|
||||
}
|
||||
compound.setTag("recipes", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("recipes", 10);
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
NBTTagCompound recipeCompound = list.getCompoundTagAt(i);
|
||||
RecipeCarpentry recipe = RecipeCarpentry.create(recipeCompound);
|
||||
recipe.readNBT(recipeCompound);
|
||||
RecipeController.syncRecipes.put(recipe.id, recipe);
|
||||
}
|
||||
|
||||
RecipeController.Instance.carpentryRecipes = RecipeController.syncRecipes;
|
||||
RecipeController.syncRecipes = new HashMap<>();
|
||||
}
|
||||
|
||||
// RELOAD-only: supportsUpdate() and supportsRemove() remain false (default)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#CHAINED_ABILITY}.
|
||||
* String-keyed family using ordered {@link LinkedHashMap} to preserve
|
||||
* registration/reload order. Supports RELOAD only for global sync.
|
||||
*/
|
||||
public class ChainedAbilitySyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (Map.Entry<String, ChainedAbility> entry : AbilityController.Instance.getChainedAbilities().entrySet()) {
|
||||
NBTTagCompound chainNBT = entry.getValue().writeNBT(false);
|
||||
chainNBT.setString("ChainedAbilityId", entry.getKey());
|
||||
list.appendTag(chainNBT);
|
||||
}
|
||||
compound.setTag("Data", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Data", 10);
|
||||
LinkedHashMap<String, ChainedAbility> sync = new LinkedHashMap<>();
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
NBTTagCompound nbt = list.getCompoundTagAt(i);
|
||||
String id = nbt.getString("ChainedAbilityId");
|
||||
ChainedAbility chain = new ChainedAbility();
|
||||
chain.readNBT(nbt);
|
||||
chain.setName(id);
|
||||
sync.put(id, chain);
|
||||
}
|
||||
AbilityController.Instance.setChainedAbilities(sync);
|
||||
}
|
||||
|
||||
// RELOAD-only for global sync: supportsUpdate() and supportsRemove() remain false (default)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#CUSTOM_ABILITY}.
|
||||
* String-keyed family using ordered {@link LinkedHashMap} to preserve
|
||||
* registration/reload order. Supports RELOAD only for global sync.
|
||||
*/
|
||||
public class CustomAbilitySyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (Map.Entry<String, Ability> entry : AbilityController.Instance.getCustomAbilities().entrySet()) {
|
||||
NBTTagCompound abilityNBT = entry.getValue().writeNBT(false);
|
||||
abilityNBT.setString("CustomAbilityId", entry.getKey());
|
||||
list.appendTag(abilityNBT);
|
||||
}
|
||||
compound.setTag("Data", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Data", 10);
|
||||
LinkedHashMap<String, Ability> sync = new LinkedHashMap<>();
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
NBTTagCompound nbt = list.getCompoundTagAt(i);
|
||||
String name = nbt.getString("CustomAbilityId");
|
||||
Ability ability = AbilityController.Instance.fromNBT(nbt);
|
||||
if (ability != null) {
|
||||
ability.setName(name);
|
||||
sync.put(name, ability);
|
||||
}
|
||||
}
|
||||
AbilityController.Instance.setCustomAbilities(sync);
|
||||
}
|
||||
|
||||
// RELOAD-only for global sync: supportsUpdate() and supportsRemove() remain false (default)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.controllers.CustomEffectController;
|
||||
import noppes.npcs.controllers.data.CustomEffect;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#CUSTOM_EFFECTS}.
|
||||
* Simple int-keyed map family with RELOAD, UPDATE, and REMOVE support.
|
||||
* Client-side RELOAD precaches effect icons via {@link ClientCacheHandler#getImageData}.
|
||||
*/
|
||||
public class CustomEffectSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (CustomEffect effect : CustomEffectController.getInstance().getCustomEffects().values()) {
|
||||
list.appendTag(effect.writeToNBT(false));
|
||||
}
|
||||
compound.setTag("Data", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Data", 10);
|
||||
CustomEffectController ce = CustomEffectController.getInstance();
|
||||
ce.customEffectsSync.clear();
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
CustomEffect effect = new CustomEffect();
|
||||
effect.readFromNBT(list.getCompoundTagAt(i));
|
||||
ClientCacheHandler.getImageData(effect.icon);
|
||||
ce.customEffectsSync.put(effect.id, effect);
|
||||
}
|
||||
|
||||
ce.getCustomEffects().clear();
|
||||
ce.getCustomEffects().putAll(ce.customEffectsSync);
|
||||
ce.customEffectsSync.clear();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
CustomEffect effect = new CustomEffect();
|
||||
effect.readFromNBT(compound);
|
||||
ClientCacheHandler.getImageData(effect.icon);
|
||||
|
||||
CustomEffectController.Instance.getCustomEffects().put(effect.id, effect);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
CustomEffectController.Instance.getCustomEffects().remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.DialogController;
|
||||
import noppes.npcs.controllers.data.Dialog;
|
||||
import noppes.npcs.controllers.data.DialogCategory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#DIALOG_CATEGORY}.
|
||||
* Parent-child category family. DIALOG children share this cache.
|
||||
* Invalidation from DIALOG or DIALOG_CATEGORY both target this cache.
|
||||
*/
|
||||
public class DialogCategorySyncHandler implements SyncHandler {
|
||||
|
||||
/**
|
||||
* Serialize a single dialog category for UPDATE packets.
|
||||
*/
|
||||
public static NBTTagCompound serializeCategory(DialogCategory dialogCategory) {
|
||||
NBTTagCompound dialogCompound = new NBTTagCompound();
|
||||
NBTTagList dialogList = new NBTTagList();
|
||||
for (int questID : dialogCategory.dialogs.keySet()) {
|
||||
Dialog dialog = dialogCategory.dialogs.get(questID);
|
||||
dialogList.appendTag(dialog.writeToNBT(new NBTTagCompound()));
|
||||
}
|
||||
dialogCompound.setTag("Data", dialogList);
|
||||
dialogCompound.setTag("CatNBT", dialogCategory.writeSmallNBT(new NBTTagCompound()));
|
||||
return dialogCompound;
|
||||
}
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
NBTTagList categoryList = new NBTTagList();
|
||||
for (DialogCategory category : DialogController.Instance.categories.values()) {
|
||||
NBTTagCompound questCompound = new NBTTagCompound();
|
||||
NBTTagList dialogList = new NBTTagList();
|
||||
for (int dialogID : category.dialogs.keySet()) {
|
||||
Dialog quest = category.dialogs.get(dialogID);
|
||||
dialogList.appendTag(quest.writeToNBT(new NBTTagCompound()));
|
||||
}
|
||||
questCompound.setTag("Data", dialogList);
|
||||
questCompound.setTag("CatNBT", category.writeSmallNBT(new NBTTagCompound()));
|
||||
categoryList.appendTag(questCompound);
|
||||
}
|
||||
compound.setTag("DialogCategories", categoryList);
|
||||
return compound;
|
||||
}
|
||||
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
if (!fullCompound.hasNoTags()) {
|
||||
NBTTagList categories = fullCompound.getTagList("DialogCategories", 10);
|
||||
for (int j = 0; j < fullCompound.getTagList("DialogCategories", 10).tagCount(); j++) {
|
||||
NBTTagCompound categoryCompound = categories.getCompoundTagAt(j);
|
||||
if (categoryCompound.hasNoTags())
|
||||
continue;
|
||||
|
||||
DialogCategory category = new DialogCategory();
|
||||
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
|
||||
NBTTagList dialogList = categoryCompound.getTagList("Data", 10);
|
||||
if (DialogController.Instance.categoriesSync.containsKey(category.id)) {
|
||||
category = DialogController.Instance.categoriesSync.get(category.id);
|
||||
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
|
||||
}
|
||||
for (int i = 0; i < dialogList.tagCount(); i++) {
|
||||
Dialog dialog = new Dialog();
|
||||
dialog.readNBT(dialogList.getCompoundTagAt(i));
|
||||
dialog.category = category;
|
||||
category.dialogs.put(dialog.id, dialog);
|
||||
}
|
||||
DialogController.Instance.categoriesSync.put(category.id, category);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<Integer, Dialog> dialogs = new HashMap<>();
|
||||
for (DialogCategory category : DialogController.Instance.categoriesSync.values()) {
|
||||
for (Dialog dialog : category.dialogs.values()) {
|
||||
dialogs.put(dialog.id, dialog);
|
||||
}
|
||||
}
|
||||
|
||||
DialogController.Instance.categories = DialogController.Instance.categoriesSync;
|
||||
DialogController.Instance.dialogs = dialogs;
|
||||
DialogController.Instance.categoriesSync = new HashMap<>();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
DialogCategory category = new DialogCategory();
|
||||
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
|
||||
NBTTagList list = compound.getTagList("Data", 10);
|
||||
if (DialogController.Instance.categoriesSync.containsKey(category.id)) {
|
||||
category = DialogController.Instance.categoriesSync.get(category.id);
|
||||
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
|
||||
}
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
Dialog dialog = new Dialog();
|
||||
dialog.readNBT(list.getCompoundTagAt(i));
|
||||
dialog.category = category;
|
||||
category.dialogs.put(dialog.id, dialog);
|
||||
}
|
||||
DialogController.Instance.categories.put(category.id, category);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
DialogCategory dialogCategory = DialogController.Instance.categories.remove(id);
|
||||
if (dialogCategory != null) {
|
||||
DialogController.Instance.dialogs.keySet().removeAll(dialogCategory.dialogs.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.controllers.DialogController;
|
||||
import noppes.npcs.controllers.data.Dialog;
|
||||
import noppes.npcs.controllers.data.DialogCategory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#DIALOG}.
|
||||
* Child family — does not have its own cache. UPDATE and REMOVE only.
|
||||
* Invalidation targets the parent {@link SyncType#DIALOG_CATEGORY} cache.
|
||||
*/
|
||||
public class DialogSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
/**
|
||||
* DIALOG does not have its own independent cache; it shares
|
||||
* the DIALOG_CATEGORY parent cache.
|
||||
*/
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
// DIALOG does not handle standalone RELOAD; it's part of DIALOG_CATEGORY
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
DialogCategory category = DialogController.Instance.categories.get(categoryId);
|
||||
if (category == null) {
|
||||
return; // Defensive: category may have been removed
|
||||
}
|
||||
Dialog dialog = new Dialog();
|
||||
dialog.category = category;
|
||||
dialog.readNBT(compound);
|
||||
DialogController.Instance.dialogs.put(dialog.id, dialog);
|
||||
category.dialogs.put(dialog.id, dialog);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
Dialog dialog = DialogController.Instance.dialogs.remove(id);
|
||||
if (dialog != null) {
|
||||
dialog.category.dialogs.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not a cached type — DIALOG shares the DIALOG_CATEGORY cache.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCachedType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SyncType> getInvalidationTargets(SyncType self) {
|
||||
// Child invalidates the parent cache
|
||||
Set<SyncType> set = new HashSet<>();
|
||||
set.add(SyncType.DIALOG_CATEGORY);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.FactionController;
|
||||
import noppes.npcs.controllers.data.Faction;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#FACTION}.
|
||||
* Simple int-keyed map family with RELOAD, UPDATE, and REMOVE support.
|
||||
*/
|
||||
public class FactionSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (Faction faction : FactionController.getInstance().factions.values()) {
|
||||
NBTTagCompound factionNBT = new NBTTagCompound();
|
||||
faction.writeNBT(factionNBT);
|
||||
list.appendTag(factionNBT);
|
||||
}
|
||||
compound.setTag("Factions", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Factions", 10);
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
Faction faction = new Faction();
|
||||
faction.readNBT(list.getCompoundTagAt(i));
|
||||
FactionController.getInstance().factionsSync.put(faction.id, faction);
|
||||
}
|
||||
FactionController.getInstance().factions = FactionController.getInstance().factionsSync;
|
||||
FactionController.getInstance().factionsSync = new HashMap<Integer, Faction>();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
Faction faction = new Faction();
|
||||
faction.readNBT(compound);
|
||||
FactionController.getInstance().factions.put(faction.id, faction);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
FactionController.getInstance().factions.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.MagicController;
|
||||
import noppes.npcs.controllers.data.MagicCycle;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#MAGIC_CYCLE}.
|
||||
* Simple int-keyed map family with RELOAD, UPDATE, and REMOVE support.
|
||||
*/
|
||||
public class MagicCycleSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (MagicCycle cycle : MagicController.getInstance().cycles.values()) {
|
||||
NBTTagCompound cycleCompound = new NBTTagCompound();
|
||||
cycle.writeNBT(cycleCompound);
|
||||
list.appendTag(cycleCompound);
|
||||
}
|
||||
compound.setTag("Data", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Data", 10);
|
||||
MagicController mc = MagicController.getInstance();
|
||||
mc.cyclesSync.clear();
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
MagicCycle cycle = new MagicCycle();
|
||||
cycle.readNBT(list.getCompoundTagAt(i));
|
||||
mc.cyclesSync.put(cycle.id, cycle);
|
||||
}
|
||||
|
||||
mc.cycles.clear();
|
||||
mc.cycles.putAll(mc.cyclesSync);
|
||||
mc.cyclesSync.clear();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
MagicCycle cycle = new MagicCycle();
|
||||
cycle.readNBT(compound);
|
||||
MagicController.getInstance().cycles.put(cycle.id, cycle);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
MagicController.getInstance().cycles.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.MagicController;
|
||||
import noppes.npcs.controllers.data.Magic;
|
||||
import noppes.npcs.controllers.data.MagicCycle;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#MAGIC}.
|
||||
* Simple int-keyed map family.
|
||||
* REMOVE dissociates cycle associations before removing the magic entry.
|
||||
*
|
||||
* <p>Note: The original code had a bug where MAGIC REMOVE incorrectly
|
||||
* removed from {@code cycles} instead of {@code magics}. This handler
|
||||
* fixes that by properly removing from {@code magics} after dissociating.</p>
|
||||
*/
|
||||
public class MagicSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (Magic magic : MagicController.getInstance().magics.values()) {
|
||||
NBTTagCompound magicCompound = new NBTTagCompound();
|
||||
magic.writeNBT(magicCompound);
|
||||
list.appendTag(magicCompound);
|
||||
}
|
||||
compound.setTag("Data", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("Data", 10);
|
||||
MagicController mc = MagicController.getInstance();
|
||||
mc.magicSync.clear();
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
Magic magic = new Magic();
|
||||
magic.readNBT(list.getCompoundTagAt(i));
|
||||
mc.magicSync.put(magic.id, magic);
|
||||
}
|
||||
|
||||
mc.magics.clear();
|
||||
mc.magics.putAll(mc.magicSync);
|
||||
mc.magicSync.clear();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
Magic magic = new Magic();
|
||||
magic.readNBT(compound);
|
||||
MagicController.getInstance().magics.put(magic.id, magic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a magic entry from the client. First dissociates cycle
|
||||
* references, then removes from the magics map.
|
||||
* <p>Bug fix: The original code incorrectly removed from {@code cycles}
|
||||
* instead of {@code magics} after dissociating cycle associations.</p>
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
// First remove this magic's association from all cycles
|
||||
for (MagicCycle cycle : MagicController.getInstance().cycles.values()) {
|
||||
cycle.associations.remove(id);
|
||||
}
|
||||
// Then remove the magic itself (fixed: was cycles.remove(id))
|
||||
MagicController.getInstance().magics.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import kamkeel.npcs.network.packets.data.ability.AbilityCooldownSyncPacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.AbilityHotbarSyncPacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.PlayerAbilitySyncPacket;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
|
||||
/**
|
||||
* Direct per-player sync helper for player ability state.
|
||||
* This is NOT a cached global sync family — it operates outside the
|
||||
* registry-driven reload/revision system.
|
||||
*
|
||||
* <p>Abilities are synced per-player on demand (e.g. ability unlock,
|
||||
* entity reconstruction, periodic cooldown ticks).</p>
|
||||
*/
|
||||
public final class PlayerAbilitySyncHelper {
|
||||
|
||||
private PlayerAbilitySyncHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Full sync of all player ability state to the client: unlocked abilities,
|
||||
* selection, toggles, hotbar layout, and cooldowns.
|
||||
* Used for entity reconstruction and other full-refresh scenarios.
|
||||
*/
|
||||
public static void syncAbilities(EntityPlayerMP playerMP) {
|
||||
PlayerData playerData = PlayerData.get(playerMP);
|
||||
if (playerData == null) return;
|
||||
|
||||
PlayerAbilitySyncPacket.sendToPlayer(playerMP);
|
||||
AbilityHotbarSyncPacket.sendToPlayer(playerMP);
|
||||
AbilityCooldownSyncPacket.sendToPlayer(playerMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight sync of cooldown state only (global + per-ability).
|
||||
* Called periodically (every 10 ticks) to keep the client's cooldown
|
||||
* display accurate. Cooldowns are transient and not part of NBT sync.
|
||||
*/
|
||||
public static void syncAbilityCooldowns(EntityPlayerMP playerMP) {
|
||||
PlayerData playerData = PlayerData.get(playerMP);
|
||||
if (playerData == null) return;
|
||||
|
||||
AbilityCooldownSyncPacket.sendToPlayer(playerMP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#PLAYERDATA}.
|
||||
* Special non-cached per-player path. Not included in login cache iteration.
|
||||
* RELOAD and UPDATE are sent as direct per-player packets.
|
||||
*/
|
||||
public class PlayerDataSyncHandler implements SyncHandler {
|
||||
|
||||
public static void syncPlayerData(EntityPlayerMP player, boolean update) {
|
||||
PlayerData data = PlayerData.get(player);
|
||||
if (data != null)
|
||||
SyncController.syncPlayerData(SyncType.PLAYERDATA, update ? data.getSyncNBTFull() : data.getSyncNBT(), update, player);
|
||||
}
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
/**
|
||||
* PLAYERDATA is not a global cached type — it is per-player.
|
||||
*/
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
if (categoryId == 1) ClientCacheHandler.playerData.setSyncNBTFull(compound);
|
||||
else ClientCacheHandler.playerData.setSyncNBT(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not a cached type — PLAYERDATA uses direct per-player sync.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCachedType() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.packets.data.large.SyncEffectPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.controllers.CustomEffectController;
|
||||
import noppes.npcs.controllers.data.EffectKey;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
import noppes.npcs.controllers.data.PlayerEffect;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Direct per-player sync helper for player effects.
|
||||
* This is NOT a cached global sync family — it operates outside the
|
||||
* registry-driven reload/revision system.
|
||||
*
|
||||
* <p>Effects are synced per-player on demand (e.g. effect application,
|
||||
* periodic ticks). The data mutates {@code playerData.effectData}
|
||||
* before sending the packet.</p>
|
||||
*/
|
||||
public final class PlayerEffectSyncHelper {
|
||||
|
||||
private PlayerEffectSyncHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side: sync the player's active effects to their client.
|
||||
* Mutates {@code playerData.effectData} before sending.
|
||||
*/
|
||||
public static void syncEffects(EntityPlayerMP playerMP) {
|
||||
ConcurrentHashMap<EffectKey, PlayerEffect> playerEffects = CustomEffectController.getInstance().getPlayerEffects(playerMP);
|
||||
PlayerData playerData = PlayerData.get(playerMP);
|
||||
playerData.effectData.setEffects(playerEffects);
|
||||
|
||||
NBTTagCompound compound = playerData.getPlayerEffects();
|
||||
PacketHandler.Instance.sendToPlayer(new SyncEffectPacket(compound), playerMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-side: apply incoming effect data to the local player.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static void clientSyncEffects(NBTTagCompound compound) {
|
||||
PlayerData playerData = PlayerData.get(Minecraft.getMinecraft().thePlayer);
|
||||
if (playerData != null) {
|
||||
playerData.setPlayerEffects(compound);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.QuestController;
|
||||
import noppes.npcs.controllers.data.Quest;
|
||||
import noppes.npcs.controllers.data.QuestCategory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#QUEST_CATEGORY}.
|
||||
* Parent-child category family. QUEST children share this cache.
|
||||
* Invalidation from QUEST or QUEST_CATEGORY both target this cache.
|
||||
*/
|
||||
public class QuestCategorySyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
NBTTagList categoryList = new NBTTagList();
|
||||
for (QuestCategory category : QuestController.Instance.categories.values()) {
|
||||
NBTTagCompound questCompound = new NBTTagCompound();
|
||||
;
|
||||
NBTTagList questList = new NBTTagList();
|
||||
for (int questID : category.quests.keySet()) {
|
||||
Quest quest = category.quests.get(questID);
|
||||
questList.appendTag(quest.writeToNBT(new NBTTagCompound()));
|
||||
}
|
||||
questCompound.setTag("Data", questList);
|
||||
questCompound.setTag("CatNBT", category.writeSmallNBT(new NBTTagCompound()));
|
||||
categoryList.appendTag(questCompound);
|
||||
}
|
||||
compound.setTag("QuestCategories", categoryList);
|
||||
return compound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a single quest category for UPDATE packets.
|
||||
*/
|
||||
public static NBTTagCompound serializeCategory(QuestCategory questCategory) {
|
||||
NBTTagCompound questCompound = new NBTTagCompound();
|
||||
NBTTagList questList = new NBTTagList();
|
||||
for (int questID : questCategory.quests.keySet()) {
|
||||
Quest quest = questCategory.quests.get(questID);
|
||||
questList.appendTag(quest.writeToNBT(new NBTTagCompound()));
|
||||
}
|
||||
questCompound.setTag("Data", questList);
|
||||
questCompound.setTag("CatNBT", questCategory.writeSmallNBT(new NBTTagCompound()));
|
||||
return questCompound;
|
||||
}
|
||||
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
if (!fullCompound.hasNoTags()) {
|
||||
NBTTagList categories = fullCompound.getTagList("QuestCategories", 10);
|
||||
for (int j = 0; j < fullCompound.getTagList("QuestCategories", 10).tagCount(); j++) {
|
||||
NBTTagCompound categoryCompound = categories.getCompoundTagAt(j);
|
||||
if (categoryCompound.hasNoTags())
|
||||
continue;
|
||||
|
||||
QuestCategory category = new QuestCategory();
|
||||
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
|
||||
NBTTagList questList = categoryCompound.getTagList("Data", 10);
|
||||
if (QuestController.Instance.categoriesSync.containsKey(category.id)) {
|
||||
category = QuestController.Instance.categoriesSync.get(category.id);
|
||||
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
|
||||
}
|
||||
for (int i = 0; i < questList.tagCount(); i++) {
|
||||
Quest quest = new Quest();
|
||||
quest.readNBT(questList.getCompoundTagAt(i));
|
||||
quest.category = category;
|
||||
category.quests.put(quest.id, quest);
|
||||
}
|
||||
QuestController.Instance.categoriesSync.put(category.id, category);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<Integer, Quest> quests = new HashMap<>();
|
||||
for (QuestCategory category : QuestController.Instance.categoriesSync.values()) {
|
||||
for (Quest quest : category.quests.values()) {
|
||||
quests.put(quest.id, quest);
|
||||
}
|
||||
}
|
||||
|
||||
QuestController.Instance.categories = QuestController.Instance.categoriesSync;
|
||||
QuestController.Instance.quests = quests;
|
||||
QuestController.Instance.categoriesSync = new HashMap<>();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
QuestCategory category = new QuestCategory();
|
||||
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
|
||||
NBTTagList list = compound.getTagList("Data", 10);
|
||||
if (QuestController.Instance.categoriesSync.containsKey(category.id)) {
|
||||
category = QuestController.Instance.categoriesSync.get(category.id);
|
||||
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
|
||||
}
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
Quest quest = new Quest();
|
||||
quest.readNBT(list.getCompoundTagAt(i));
|
||||
quest.category = category;
|
||||
category.quests.put(quest.id, quest);
|
||||
}
|
||||
QuestController.Instance.categories.put(category.id, category);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
QuestCategory questCategory = QuestController.Instance.categories.remove(id);
|
||||
if (questCategory != null) {
|
||||
QuestController.Instance.quests.keySet().removeAll(questCategory.quests.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.controllers.QuestController;
|
||||
import noppes.npcs.controllers.data.Quest;
|
||||
import noppes.npcs.controllers.data.QuestCategory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#QUEST}.
|
||||
* Child family — does not have its own cache. UPDATE and REMOVE only.
|
||||
* Invalidation targets the parent {@link SyncType#QUEST_CATEGORY} cache.
|
||||
*/
|
||||
public class QuestSyncHandler implements SyncHandler {
|
||||
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
/**
|
||||
* QUEST does not have its own independent cache; it shares
|
||||
* the QUEST_CATEGORY parent cache.
|
||||
*/
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
// QUEST does not handle standalone RELOAD; it's part of QUEST_CATEGORY
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleUpdate(NBTTagCompound compound, int categoryId) {
|
||||
QuestCategory category = QuestController.Instance.categories.get(categoryId);
|
||||
if (category == null)
|
||||
return; // Defensive: category may have been removed
|
||||
|
||||
Quest quest = new Quest();
|
||||
quest.category = category;
|
||||
quest.readNBT(compound);
|
||||
QuestController.Instance.quests.put(quest.id, quest);
|
||||
category.quests.put(quest.id, quest);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleRemove(int id) {
|
||||
Quest quest = QuestController.Instance.quests.remove(id);
|
||||
if (quest != null)
|
||||
quest.category.quests.remove(id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not a cached type — QUEST shares the QUEST_CATEGORY cache.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCachedType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SyncType> getInvalidationTargets(SyncType self) {
|
||||
// Child invalidates the parent cache
|
||||
Set<SyncType> set = new HashSet<>();
|
||||
set.add(SyncType.QUEST_CATEGORY);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package kamkeel.npcs.controllers.sync.handlers;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.controllers.RecipeController;
|
||||
import noppes.npcs.controllers.data.RecipeCarpentry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sync handler for {@link SyncType#WORKBENCH_RECIPES}.
|
||||
* RELOAD-only family — does not support individual UPDATE or REMOVE.
|
||||
* Client-side RELOAD routes through {@link RecipeController#reloadGlobalRecipes}.
|
||||
*/
|
||||
public class WorkbenchRecipeSyncHandler implements SyncHandler {
|
||||
|
||||
private static final WorkbenchRecipeSyncHandler INSTANCE = new WorkbenchRecipeSyncHandler();
|
||||
|
||||
public static WorkbenchRecipeSyncHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
// ========== SERVER-SIDE ==========
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeAll() {
|
||||
RecipeController controller = RecipeController.Instance;
|
||||
NBTTagList list = new NBTTagList();
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
for (RecipeCarpentry recipe : controller.globalRecipes.values()) {
|
||||
list.appendTag(recipe.writeNBT(false));
|
||||
}
|
||||
compound.setTag("recipes", list);
|
||||
return compound;
|
||||
}
|
||||
|
||||
// ========== CLIENT-SIDE ==========
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void clientHandleReload(NBTTagCompound fullCompound) {
|
||||
NBTTagList list = fullCompound.getTagList("recipes", 10);
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
NBTTagCompound recipeCompound = list.getCompoundTagAt(i);
|
||||
RecipeCarpentry recipe = RecipeCarpentry.create(recipeCompound);
|
||||
recipe.readNBT(recipeCompound);
|
||||
RecipeController.syncRecipes.put(recipe.id, recipe);
|
||||
}
|
||||
|
||||
RecipeController.reloadGlobalRecipes(RecipeController.syncRecipes);
|
||||
RecipeController.syncRecipes = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SyncType> getInvalidationTargets(SyncType self) {
|
||||
Set<SyncType> set = new HashSet<>();
|
||||
set.add(SyncType.WORKBENCH_RECIPES);
|
||||
return set;
|
||||
}
|
||||
|
||||
// RELOAD-only: supportsUpdate() and supportsRemove() remain false (default)
|
||||
}
|
||||
@@ -264,18 +264,21 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
Vec3 anchorPos = AnchorPointHelper.calculateAnchorPosition(livingOwner, anchorData);
|
||||
setPosition(anchorPos.xCoord, anchorPos.yCoord, anchorPos.zCoord);
|
||||
|
||||
Entity targetEntity = getTargetEntity();
|
||||
if (targetEntity instanceof EntityLivingBase && targetEntity.isEntityAlive()) {
|
||||
EntityLivingBase target = (EntityLivingBase) targetEntity;
|
||||
double dx = target.posX - anchorPos.xCoord;
|
||||
double dy = (target.posY + target.getEyeHeight() - 0.4) - anchorPos.yCoord;
|
||||
double dz = target.posZ - anchorPos.zCoord;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0.0001) {
|
||||
dirX = dx / len;
|
||||
dirY = dy / len;
|
||||
dirZ = dz / len;
|
||||
}
|
||||
// Converge on the point the owner is aiming at, not on the target itself. Aiming
|
||||
// straight at the target would re-derive the direction every tick and ignore the
|
||||
// rotation the owner is actually allowed to reach, which is what track speed and
|
||||
// rotation lock constrain.
|
||||
double aimX = livingOwner.posX + dirX * maxLength;
|
||||
double aimY = livingOwner.posY + livingOwner.getEyeHeight() + dirY * maxLength;
|
||||
double aimZ = livingOwner.posZ + dirZ * maxLength;
|
||||
double dx = aimX - anchorPos.xCoord;
|
||||
double dy = aimY - anchorPos.yCoord;
|
||||
double dz = aimZ - anchorPos.zCoord;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0.0001) {
|
||||
dirX = dx / len;
|
||||
dirY = dy / len;
|
||||
dirZ = dz / len;
|
||||
}
|
||||
} else {
|
||||
Vec3 direction = Vec3.createVectorHelper(dirX, dirY, dirZ);
|
||||
|
||||
@@ -155,6 +155,9 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
|
||||
// HAZARD-SPECIFIC PROPERTIES
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Floor on how often a hazard pulses, so a small interval cannot turn into a per-tick scan. */
|
||||
public static final int MIN_DAMAGE_INTERVAL = 5;
|
||||
|
||||
private float damagePerSecond = 1.0f;
|
||||
private int damageInterval = 20;
|
||||
private boolean ignoreIFrames = false;
|
||||
@@ -291,7 +294,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
|
||||
zone.shape = shape;
|
||||
zone.radius = radius;
|
||||
zone.damagePerSecond = damagePerSecond;
|
||||
zone.damageInterval = damageInterval;
|
||||
zone.damageInterval = Math.max(MIN_DAMAGE_INTERVAL, damageInterval);
|
||||
zone.ignoreIFrames = ignoreIFrames;
|
||||
zone.affectsCaster = affectsCaster;
|
||||
zone.durationTicks = durationTicks;
|
||||
@@ -309,7 +312,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
|
||||
zone.effects.add(e.copy());
|
||||
}
|
||||
}
|
||||
zone.ticksSinceDamage = damageInterval;
|
||||
return zone;
|
||||
}
|
||||
|
||||
@@ -518,7 +520,9 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
|
||||
if (!isInZone(entity)) continue;
|
||||
|
||||
if (damagePerSecond > 0) {
|
||||
applyDamage(entity, owner, damagePerSecond);
|
||||
// The configured value is damage per second, so a pulse deals the share of a
|
||||
// second it covers. Applying it whole made the real rate depend on the interval.
|
||||
applyDamage(entity, owner, damagePerSecond * (damageInterval / 20.0f));
|
||||
}
|
||||
|
||||
applyEffects(entity);
|
||||
@@ -815,7 +819,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
|
||||
public float getDamagePerSecond() { return damagePerSecond; }
|
||||
public void setDamagePerSecond(float dps) { this.damagePerSecond = dps; }
|
||||
public int getDamageInterval() { return damageInterval; }
|
||||
public void setDamageInterval(int ticks) { this.damageInterval = ticks; }
|
||||
public void setDamageInterval(int ticks) { this.damageInterval = Math.max(MIN_DAMAGE_INTERVAL, ticks); }
|
||||
public boolean isAffectsCaster() { return affectsCaster; }
|
||||
public void setAffectsCaster(boolean affects) { this.affectsCaster = affects; }
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.addon.DBCAddon;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.network.packets.data.energy.BarrierClientSyncPacket;
|
||||
@@ -15,6 +17,7 @@ import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.CustomNpcs;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.config.ConfigMain;
|
||||
import noppes.npcs.controllers.data.MagicData;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
import noppes.npcs.entity.EntityNPCInterface;
|
||||
@@ -114,6 +117,8 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
protected EnergyBarrierData barrierData = new EnergyBarrierData();
|
||||
protected float currentHealth;
|
||||
protected int ticksAlive = 0;
|
||||
/** Absolute world time the barrier expires at, so ticks it misses cannot extend it. */
|
||||
protected long barrierDeathTime = -1;
|
||||
|
||||
// ==================== DATA WATCHER INDICES ====================
|
||||
protected static final int DW_HEALTH_PERCENT = 21;
|
||||
@@ -436,8 +441,19 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
|
||||
float damage = amount * barrierData.meleeDamageMultiplier;
|
||||
|
||||
// Apply magic interactions from attacker's magic vs barrier's magic
|
||||
Entity attacker = source.getEntity();
|
||||
|
||||
// A barrier is a plain Entity, so no LivingHurtEvent fires for it and nothing scales the
|
||||
// hit the way it would against a living target - vanilla hands us the bare attackDamage
|
||||
// attribute. Run the scaling here instead, or the barrier only ever takes a point of it.
|
||||
if (attacker instanceof EntityLivingBase) {
|
||||
damage = AbilityController.Instance.fireModifyBarrierMeleeDamage(this, (EntityLivingBase) attacker, damage);
|
||||
}
|
||||
if (attacker instanceof EntityPlayer && ConfigMain.AttributesEnabled && !DBCAddon.IsAvailable()) {
|
||||
damage = AttributeAttackUtil.calculateOutgoing((EntityPlayer) attacker, damage);
|
||||
}
|
||||
|
||||
// Apply magic interactions from attacker's magic vs barrier's magic
|
||||
if (attacker != null) {
|
||||
MagicData attackerMagic = null;
|
||||
if (attacker instanceof EntityPlayer) {
|
||||
@@ -510,15 +526,16 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
}
|
||||
}
|
||||
|
||||
// Duration check
|
||||
if (barrierData.useDuration && ticksAlive >= barrierData.durationTicks) {
|
||||
onBarrierDestroyed();
|
||||
this.setDead();
|
||||
return true;
|
||||
// Set the deadline on the first tick after charging, so lifetime starts when the
|
||||
// barrier is actually up. Counting elapsed ticks instead would let any tick the
|
||||
// entity misses - chunk unload, for one - extend it past its configured duration.
|
||||
if (barrierDeathTime < 0 && !isCharging()) {
|
||||
barrierDeathTime = worldObj.getTotalWorldTime()
|
||||
+ (barrierData.useDuration ? barrierData.durationTicks : BARRIER_HARD_LIFETIME_CAP);
|
||||
}
|
||||
|
||||
// Absolute hard lifetime cap (safety net for barriers with no duration)
|
||||
if (ticksAlive > BARRIER_HARD_LIFETIME_CAP) {
|
||||
// Duration check, and the hard lifetime cap for barriers with no duration
|
||||
if (barrierDeathTime >= 0 && worldObj.getTotalWorldTime() >= barrierDeathTime) {
|
||||
onBarrierDestroyed();
|
||||
this.setDead();
|
||||
return true;
|
||||
@@ -865,6 +882,7 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
protected void writeBarrierBaseNBT(NBTTagCompound nbt) {
|
||||
writeEnergyBaseNBT(nbt);
|
||||
nbt.setInteger("TicksAlive", ticksAlive);
|
||||
nbt.setLong("BarrierDeathTime", barrierDeathTime);
|
||||
nbt.setFloat("CurrentHealth", currentHealth);
|
||||
nbt.setBoolean("Charging", charging);
|
||||
nbt.setInteger("ChargeTick", chargeTick);
|
||||
@@ -879,6 +897,7 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
protected void readBarrierBaseNBT(NBTTagCompound nbt) {
|
||||
readEnergyBaseNBT(nbt);
|
||||
this.ticksAlive = nbt.getInteger("TicksAlive");
|
||||
this.barrierDeathTime = nbt.hasKey("BarrierDeathTime") ? nbt.getLong("BarrierDeathTime") : -1;
|
||||
this.currentHealth = nbt.getFloat("CurrentHealth");
|
||||
if (Float.isNaN(currentHealth) || Float.isInfinite(currentHealth) || currentHealth < 0) currentHealth = barrierData.maxHealth;
|
||||
this.charging = nbt.hasKey("Charging") && nbt.getBoolean("Charging");
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package kamkeel.npcs.network.enums;
|
||||
|
||||
public enum EnumSyncType {
|
||||
FACTION,
|
||||
DIALOG,
|
||||
DIALOG_CATEGORY,
|
||||
QUEST,
|
||||
QUEST_CATEGORY,
|
||||
PLAYERDATA,
|
||||
MAGIC,
|
||||
MAGIC_CYCLE,
|
||||
WORKBENCH_RECIPES,
|
||||
CARPENTRY_RECIPES,
|
||||
ANVIL_RECIPES,
|
||||
CUSTOM_EFFECTS,
|
||||
CUSTOM_ABILITY,
|
||||
CHAINED_ABILITY
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package kamkeel.npcs.network.enums;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Immutable, object-based sync-type identity for the CNPC+ sync backend.
|
||||
*
|
||||
* <p>Each {@code SyncType} has a canonical namespaced string ID ({@code modid:path})
|
||||
* and is registered via the static {@link #register} factory. Built-in CNPC+
|
||||
* families are exposed as {@code public static final} constants.</p>
|
||||
*
|
||||
* <p>Identity is based on the canonical {@link #id} string, which is unique
|
||||
* across all registered types. Two references with the same {@code id}
|
||||
* are always the same object (identity == equality for registered types).</p>
|
||||
*
|
||||
* <p>The {@code ordinal} field provides a stable integer identity for
|
||||
* wire encoding during the migration period. It is assigned at registration
|
||||
* time in declaration order and must NOT be used as a protocol identifier
|
||||
* once string-on-wire migration is complete.</p>
|
||||
*/
|
||||
public final class SyncType {
|
||||
|
||||
// ──────────────────── Registry ────────────────────
|
||||
private static final Map<String, SyncType> REGISTRY = new LinkedHashMap<>();
|
||||
private static int nextOrdinal = 0;
|
||||
|
||||
// ──────────────────── CNPC+ families ────────────────────
|
||||
public static final SyncType FACTION = register("cnpc:faction");
|
||||
public static final SyncType DIALOG = register("cnpc:dialog");
|
||||
public static final SyncType DIALOG_CATEGORY = register("cnpc:dialog_category");
|
||||
public static final SyncType QUEST = register("cnpc:quest");
|
||||
public static final SyncType QUEST_CATEGORY = register("cnpc:quest_category");
|
||||
public static final SyncType PLAYERDATA = register("cnpc:playerdata");
|
||||
public static final SyncType MAGIC = register("cnpc:magic");
|
||||
public static final SyncType MAGIC_CYCLE = register("cnpc:magic_cycle");
|
||||
public static final SyncType WORKBENCH_RECIPES = register("cnpc:workbench_recipes");
|
||||
public static final SyncType CARPENTRY_RECIPES = register("cnpc:carpentry_recipes");
|
||||
public static final SyncType ANVIL_RECIPES = register("cnpc:anvil_recipes");
|
||||
public static final SyncType CUSTOM_EFFECTS = register("cnpc:custom_effects");
|
||||
public static final SyncType CUSTOM_ABILITY = register("cnpc:custom_ability");
|
||||
public static final SyncType CHAINED_ABILITY = register("cnpc:chained_ability");
|
||||
|
||||
// ──────────────────── Instance fields ────────────────────
|
||||
private final String id;
|
||||
private final int ordinal;
|
||||
|
||||
private SyncType(String id, int ordinal) {
|
||||
this.id = id;
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
|
||||
// ──────────────────── Public API ────────────────────
|
||||
|
||||
/**
|
||||
* The canonical namespaced identifier, e.g. {@code "cnpc:faction"}.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable integer ordinal assigned at registration time.
|
||||
* Used for wire encoding during the migration period only.
|
||||
*/
|
||||
public int ordinal() {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
// ──────────────────── Registration ────────────────────
|
||||
|
||||
/**
|
||||
* Register a new {@code SyncType} with the given canonical ID.
|
||||
*
|
||||
* @param id namespaced identifier ({@code modid:path})
|
||||
* @return the newly created and registered {@code SyncType}
|
||||
* @throws IllegalArgumentException if the ID is null, empty, or lacks a colon
|
||||
* @throws IllegalStateException if a type with the same ID is already registered
|
||||
* @throws IllegalStateException if the registry has been frozen
|
||||
*/
|
||||
public static SyncType register(String id) {
|
||||
if (id == null || id.isEmpty()) throw new IllegalArgumentException("SyncType id must not be null or empty");
|
||||
if (!id.contains(":")) throw new IllegalArgumentException("SyncType id must be namespaced (modid:path): " + id);
|
||||
if (REGISTRY.containsKey(id)) throw new IllegalStateException("Duplicate SyncType registration: " + id);
|
||||
|
||||
SyncType type = new SyncType(id, nextOrdinal++);
|
||||
REGISTRY.put(id, type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a registered SyncType by its canonical string ID.
|
||||
*
|
||||
* @return the registered type, or {@code null} if not found
|
||||
*/
|
||||
public static SyncType byId(String id) {
|
||||
return id == null ? null : REGISTRY.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a registered SyncType by its ordinal.
|
||||
* Used during migration for decoding ordinal-based wire traffic.
|
||||
*
|
||||
* @return the registered type, or {@code null} if ordinal is out of range
|
||||
*/
|
||||
public static SyncType byOrdinal(int ordinal) {
|
||||
if (ordinal < 0) return null;
|
||||
|
||||
for (SyncType type : REGISTRY.values())
|
||||
if (type.ordinal == ordinal) return type;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all registered SyncType instances in registration order.
|
||||
*/
|
||||
public static Collection<SyncType> values() {
|
||||
return Collections.unmodifiableCollection(REGISTRY.values());
|
||||
}
|
||||
|
||||
// ──────────────────── Object overrides ────────────────────
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof SyncType)) return false;
|
||||
return id.equals(((SyncType) o).id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumDataPacket;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.network.packets.player.SyncRevisionInfoPacket;
|
||||
import kamkeel.npcs.util.ByteBufUtils;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -21,19 +21,20 @@ import noppes.npcs.controllers.AuctionConfigSync;
|
||||
import noppes.npcs.controllers.ScriptConfigSync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class LoginPacket extends AbstractPacket {
|
||||
public static final String packetName = "CNPC+|Login";
|
||||
|
||||
private String serverCacheKey = "";
|
||||
private final EnumMap<EnumSyncType, Integer> revisionSnapshot = new EnumMap<>(EnumSyncType.class);
|
||||
private final Map<SyncType, Integer> revisionSnapshot = new LinkedHashMap<>();
|
||||
|
||||
public LoginPacket() {
|
||||
}
|
||||
|
||||
public LoginPacket(String serverCacheKey, EnumMap<EnumSyncType, Integer> revisionSnapshot) {
|
||||
public LoginPacket(String serverCacheKey, Map<SyncType, Integer> revisionSnapshot) {
|
||||
if (serverCacheKey != null) {
|
||||
this.serverCacheKey = serverCacheKey;
|
||||
}
|
||||
@@ -58,17 +59,15 @@ public final class LoginPacket extends AbstractPacket {
|
||||
out.writeBoolean(ConfigMain.ProfilesEnabled);
|
||||
ByteBufUtils.writeString(out, serverCacheKey);
|
||||
out.writeShort(revisionSnapshot.size());
|
||||
for (Map.Entry<EnumSyncType, Integer> entry : revisionSnapshot.entrySet()) {
|
||||
for (Map.Entry<SyncType, Integer> entry : revisionSnapshot.entrySet()) {
|
||||
out.writeInt(entry.getKey().ordinal());
|
||||
out.writeInt(entry.getValue());
|
||||
}
|
||||
|
||||
// Send auction config (server-side sync)
|
||||
NBTTagCompound auctionConfig = new NBTTagCompound();
|
||||
AuctionConfigSync.writeToNBT(auctionConfig);
|
||||
ByteBufUtils.writeNBT(out, auctionConfig);
|
||||
|
||||
// Send script config (server-side sync)
|
||||
NBTTagCompound scriptConfig = new NBTTagCompound();
|
||||
ScriptConfigSync.writeToNBT(scriptConfig);
|
||||
ByteBufUtils.writeNBT(out, scriptConfig);
|
||||
@@ -82,20 +81,20 @@ public final class LoginPacket extends AbstractPacket {
|
||||
|
||||
String serverKey = ByteBufUtils.readString(in);
|
||||
int revisionCount = in.readUnsignedShort();
|
||||
EnumMap<EnumSyncType, Integer> serverRevisions = new EnumMap<>(EnumSyncType.class);
|
||||
Map<SyncType, Integer> serverRevisions = new LinkedHashMap<>();
|
||||
for (int i = 0; i < revisionCount; i++) {
|
||||
EnumSyncType type = EnumSyncType.values()[in.readInt()];
|
||||
SyncType type = SyncType.byOrdinal(in.readInt());
|
||||
int revision = in.readInt();
|
||||
serverRevisions.put(type, revision);
|
||||
if (type != null) {
|
||||
serverRevisions.put(type, revision);
|
||||
}
|
||||
}
|
||||
|
||||
ClientCacheHandler.setActiveServer(serverKey, serverRevisions);
|
||||
|
||||
// Read auction config
|
||||
NBTTagCompound auctionConfig = ByteBufUtils.readNBT(in);
|
||||
AuctionClientConfig.readFromNBT(auctionConfig);
|
||||
|
||||
// Read script config
|
||||
NBTTagCompound scriptConfig = ByteBufUtils.readNBT(in);
|
||||
ScriptClientConfig.readFromNBT(scriptConfig);
|
||||
|
||||
|
||||
@@ -9,13 +9,9 @@ import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumDataPacket;
|
||||
import kamkeel.npcs.util.ByteBufUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.client.gui.util.GuiContainerNPCInterface;
|
||||
import noppes.npcs.client.gui.util.GuiNPCInterface;
|
||||
import noppes.npcs.client.gui.util.IGuiData;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -59,15 +55,6 @@ public final class GuiDataPacket extends LargeAbstractPacket {
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
protected void handleCompleteData(ByteBuf data, EntityPlayer player) throws IOException {
|
||||
GuiScreen gui = Minecraft.getMinecraft().currentScreen;
|
||||
if (gui instanceof GuiNPCInterface && ((GuiNPCInterface) gui).hasSubGui()) {
|
||||
gui = ((GuiNPCInterface) gui).getSubGui();
|
||||
} else if (gui instanceof GuiContainerNPCInterface && ((GuiContainerNPCInterface) gui).hasSubGui()) {
|
||||
gui = ((GuiContainerNPCInterface) gui).getSubGui();
|
||||
}
|
||||
if (gui instanceof IGuiData) {
|
||||
NBTTagCompound nbt = ByteBufUtils.readBigNBT(data);
|
||||
((IGuiData) gui).setGuiData(nbt);
|
||||
}
|
||||
IGuiData.legacySetGuiData(ByteBufUtils.readBigNBT(data));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerEffectSyncHelper;
|
||||
import kamkeel.npcs.network.LargeAbstractPacket;
|
||||
import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
@@ -54,7 +54,7 @@ public final class SyncEffectPacket extends LargeAbstractPacket {
|
||||
return;
|
||||
try {
|
||||
NBTTagCompound tag = ByteBufUtils.readBigNBT(data);
|
||||
SyncController.clientSyncEffects(tag);
|
||||
PlayerEffectSyncHelper.clientSyncEffects(tag);
|
||||
} catch (RuntimeException e) {
|
||||
LogWriter.error(String.format("Attempted to Sync Effects but it was too big"));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumDataPacket;
|
||||
import kamkeel.npcs.network.enums.EnumSyncAction;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.util.ByteBufUtils;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -22,16 +22,13 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A large sync packet that sends chunked data to the client for a given SyncType
|
||||
* along with a SyncAction (RELOAD, UPDATE, REMOVE).
|
||||
*/
|
||||
public final class SyncPacket extends LargeAbstractPacket {
|
||||
|
||||
private EnumSyncType enumSyncType;
|
||||
private SyncType syncType;
|
||||
private EnumSyncAction enumSyncAction;
|
||||
private NBTTagCompound syncData;
|
||||
private int operationID;
|
||||
private String operationKey;
|
||||
private int revision = -1;
|
||||
private byte[] cachedPayload;
|
||||
private byte[][] cachedChunks;
|
||||
@@ -39,23 +36,21 @@ public final class SyncPacket extends LargeAbstractPacket {
|
||||
public SyncPacket() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new LargeSyncPacket.
|
||||
*/
|
||||
public SyncPacket(EnumSyncType enumSyncType, EnumSyncAction enumSyncAction, int catId, NBTTagCompound syncData) {
|
||||
this(enumSyncType, enumSyncAction, catId, -1, syncData);
|
||||
public SyncPacket(SyncType syncType, EnumSyncAction enumSyncAction, int catId, NBTTagCompound syncData) {
|
||||
this(syncType, enumSyncAction, catId, null, -1, syncData);
|
||||
}
|
||||
|
||||
public SyncPacket(EnumSyncType enumSyncType, EnumSyncAction enumSyncAction, int catId, int revision, NBTTagCompound syncData) {
|
||||
this.enumSyncType = enumSyncType;
|
||||
public SyncPacket(SyncType syncType, EnumSyncAction enumSyncAction, int operationId, String operationKey, int revision, NBTTagCompound syncData) {
|
||||
this.syncType = syncType;
|
||||
this.enumSyncAction = enumSyncAction;
|
||||
this.syncData = syncData;
|
||||
this.operationID = catId;
|
||||
this.operationID = operationId;
|
||||
this.operationKey = operationKey;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public SyncPacket(EnumSyncType enumSyncType, SyncController.CachedSyncPayload payload) {
|
||||
this.enumSyncType = enumSyncType;
|
||||
public SyncPacket(SyncType syncType, SyncController.CachedSyncPayload payload) {
|
||||
this.syncType = syncType;
|
||||
this.enumSyncAction = EnumSyncAction.RELOAD;
|
||||
this.operationID = -1;
|
||||
this.revision = payload.getRevision();
|
||||
@@ -79,13 +74,21 @@ public final class SyncPacket extends LargeAbstractPacket {
|
||||
return cachedPayload;
|
||||
}
|
||||
|
||||
boolean hasKey = operationKey != null;
|
||||
boolean hasNbt = syncData != null;
|
||||
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
try {
|
||||
buffer.writeInt(enumSyncType.ordinal());
|
||||
buffer.writeInt(syncType.ordinal());
|
||||
buffer.writeInt(enumSyncAction.ordinal());
|
||||
buffer.writeInt(operationID);
|
||||
buffer.writeInt(revision);
|
||||
ByteBufUtils.writeBigNBT(buffer, syncData);
|
||||
|
||||
// UPDATE/REMOVE UNIQUE FIELDS
|
||||
buffer.writeBoolean(hasKey);
|
||||
if (hasKey) ByteBufUtils.writeString(buffer, operationKey);
|
||||
buffer.writeBoolean(hasNbt);
|
||||
if (hasNbt) ByteBufUtils.writeBigNBT(buffer, syncData);
|
||||
|
||||
byte[] bytes = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(bytes);
|
||||
@@ -131,39 +134,40 @@ public final class SyncPacket extends LargeAbstractPacket {
|
||||
if (CustomNpcs.side() != Side.CLIENT)
|
||||
return;
|
||||
|
||||
// Reconstruct everything from the ByteBuf
|
||||
int syncTypeOrdinal = data.readInt();
|
||||
int syncActionOrdinal = data.readInt();
|
||||
int categoryID = data.readInt();
|
||||
int incomingRevision = data.readInt();
|
||||
operationID = data.readInt();
|
||||
revision = data.readInt();
|
||||
|
||||
EnumSyncType type = EnumSyncType.values()[syncTypeOrdinal];
|
||||
EnumSyncAction action = EnumSyncAction.values()[syncActionOrdinal];
|
||||
try {
|
||||
NBTTagCompound tag = ByteBufUtils.readBigNBT(data);
|
||||
// Now do your client-side logic (similar to your old clientSync() or clientSyncUpdate() approach)
|
||||
handleSyncPacketClient(type, action, categoryID, incomingRevision, tag);
|
||||
} catch (RuntimeException e) {
|
||||
LogWriter.error(String.format("Attempted to Sync %s but it was too big", type.toString()));
|
||||
|
||||
// These fields only exist for UPDATE/REMOVE
|
||||
if (action != EnumSyncAction.RELOAD) {
|
||||
if (data.readBoolean()) operationKey = ByteBufUtils.readString(data);
|
||||
if (data.readBoolean()) syncData = ByteBufUtils.readBigNBT(data);
|
||||
} else
|
||||
syncData = ByteBufUtils.readBigNBT(data);
|
||||
|
||||
|
||||
syncType = SyncType.byOrdinal(syncTypeOrdinal);
|
||||
if (syncType == null) {
|
||||
LogWriter.error("[SyncPacket] Unknown sync type ordinal: " + syncTypeOrdinal + "; skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
handleSync(syncType, action, operationID, operationKey, revision, syncData);
|
||||
}
|
||||
|
||||
private void handleSyncPacketClient(
|
||||
EnumSyncType enumSyncType,
|
||||
EnumSyncAction enumSyncAction,
|
||||
int id,
|
||||
int incomingRevision,
|
||||
NBTTagCompound data
|
||||
) {
|
||||
switch (enumSyncAction) {
|
||||
private void handleSync(SyncType type, EnumSyncAction action, int id, String key, int revision, NBTTagCompound data) {
|
||||
switch (action) {
|
||||
case RELOAD:
|
||||
SyncController.clientSync(enumSyncType, incomingRevision, data);
|
||||
SyncController.clientHandleAll(type, revision, data);
|
||||
break;
|
||||
case UPDATE:
|
||||
SyncController.clientUpdate(enumSyncType, id, incomingRevision, data);
|
||||
SyncController.clientHandleUpdate(type, id, key, revision, data);
|
||||
break;
|
||||
case REMOVE:
|
||||
SyncController.clientSyncRemove(enumSyncType, id, incomingRevision);
|
||||
SyncController.clientHandleRemove(type, id, key, revision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ import kamkeel.npcs.network.AbstractPacket;
|
||||
import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumPlayerPacket;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.util.ByteBufUtils;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SyncRevisionInfoPacket extends AbstractPacket {
|
||||
@@ -23,12 +23,12 @@ public class SyncRevisionInfoPacket extends AbstractPacket {
|
||||
|
||||
private String serverKey = "";
|
||||
private String previousServerKey = "";
|
||||
private final EnumMap<EnumSyncType, Integer> revisions = new EnumMap<>(EnumSyncType.class);
|
||||
private final Map<SyncType, Integer> revisions = new LinkedHashMap<>();
|
||||
|
||||
public SyncRevisionInfoPacket() {
|
||||
}
|
||||
|
||||
public SyncRevisionInfoPacket(String serverKey, String previousServerKey, EnumMap<EnumSyncType, Integer> revisions) {
|
||||
public SyncRevisionInfoPacket(String serverKey, String previousServerKey, Map<SyncType, Integer> revisions) {
|
||||
if (serverKey != null) {
|
||||
this.serverKey = serverKey;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class SyncRevisionInfoPacket extends AbstractPacket {
|
||||
ByteBufUtils.writeString(out, serverKey);
|
||||
ByteBufUtils.writeString(out, previousServerKey);
|
||||
out.writeShort(revisions.size());
|
||||
for (Map.Entry<EnumSyncType, Integer> entry : revisions.entrySet()) {
|
||||
for (Map.Entry<SyncType, Integer> entry : revisions.entrySet()) {
|
||||
out.writeInt(entry.getKey().ordinal());
|
||||
out.writeInt(entry.getValue());
|
||||
}
|
||||
@@ -71,13 +71,15 @@ public class SyncRevisionInfoPacket extends AbstractPacket {
|
||||
String incomingServerKey = ByteBufUtils.readString(in);
|
||||
String incomingPreviousKey = ByteBufUtils.readString(in);
|
||||
int revisionCount = in.readUnsignedShort();
|
||||
EnumMap<EnumSyncType, Integer> incomingRevisions = new EnumMap<>(EnumSyncType.class);
|
||||
Map<SyncType, Integer> incomingRevisions = new LinkedHashMap<>();
|
||||
for (int i = 0; i < revisionCount; i++) {
|
||||
EnumSyncType type = EnumSyncType.values()[in.readInt()];
|
||||
SyncType type = SyncType.byOrdinal(in.readInt());
|
||||
int revision = in.readInt();
|
||||
incomingRevisions.put(type, revision);
|
||||
if (type != null) {
|
||||
incomingRevisions.put(type, revision);
|
||||
}
|
||||
}
|
||||
|
||||
SyncController.handleClientRevisionReport((EntityPlayerMP) player, incomingServerKey, incomingPreviousKey, incomingRevisions);
|
||||
SyncController.completeLoginRevisionHandshake((EntityPlayerMP) player, incomingServerKey, incomingPreviousKey, incomingRevisions);
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -50,10 +50,9 @@ public final class AbilityHotbarSelectPacket extends AbstractPacket {
|
||||
// Deselect
|
||||
data.abilityData.setSelectedIndex(-1);
|
||||
} else {
|
||||
int index = data.abilityData.getUnlockedAbilityList().indexOf(key);
|
||||
if (index >= 0) {
|
||||
data.abilityData.setSelectedIndex(index);
|
||||
}
|
||||
// A key the player no longer has deselects rather than leaving the previous
|
||||
// selection in place, which would fire a different ability than the one shown.
|
||||
data.abilityData.setSelectedIndex(data.abilityData.getUnlockedAbilityList().indexOf(key));
|
||||
}
|
||||
data.save();
|
||||
}
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ public final class CategoryItemsRequestPacket extends AbstractPacket {
|
||||
|
||||
// Send tag map for the items
|
||||
HashMap<String, HashSet<UUID>> tagMap = getTagMapByCat(type, categoryId);
|
||||
if (tagMap != null && !tagMap.isEmpty()) {
|
||||
if (tagMap != null) {
|
||||
TagController.sendCategoryTagMap((EntityPlayerMP) player, tagMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.CustomNpcsPermissions;
|
||||
import noppes.npcs.roles.RoleTrader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -63,7 +64,13 @@ public final class RoleSavePacket extends AbstractPacket {
|
||||
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
|
||||
return;
|
||||
|
||||
npc.roleInterface.readFromNBT(ByteBufUtils.readNBT(in));
|
||||
NBTTagCompound roleCompound = ByteBufUtils.readNBT(in);
|
||||
if (npc.roleInterface instanceof RoleTrader) {
|
||||
// An admin edit wins over the linked market; the market is written from it afterwards.
|
||||
((RoleTrader) npc.roleInterface).readFromNBT(roleCompound, false);
|
||||
} else {
|
||||
npc.roleInterface.readFromNBT(roleCompound);
|
||||
}
|
||||
npc.updateClient = true;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import kamkeel.npcs.network.AbstractPacket;
|
||||
import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.PacketUtil;
|
||||
import kamkeel.npcs.network.enums.EnumItemPacketType;
|
||||
import kamkeel.npcs.network.enums.EnumRequestPacket;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -38,9 +36,6 @@ public final class TagsGetPacket extends AbstractPacket {
|
||||
|
||||
@Override
|
||||
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
|
||||
if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.WAND, EnumItemPacketType.CLONER))
|
||||
return;
|
||||
|
||||
NoppesUtilServer.sendTagDataAll((EntityPlayerMP) player);
|
||||
}
|
||||
|
||||
|
||||
@@ -786,7 +786,15 @@ public abstract class AbstractDataAbilities {
|
||||
fireTickEvent(currentAbility, target);
|
||||
|
||||
// Script may have completed/cancelled the ability during the tick event
|
||||
if (currentAbility == null || !currentAbility.isExecuting()) return;
|
||||
if (currentAbility == null) return;
|
||||
if (!currentAbility.isExecuting()) {
|
||||
// tick() itself can end the ability (DAZED -> IDLE). Run its completion here,
|
||||
// otherwise it stays the current ability forever and nothing else can start.
|
||||
if (phaseChanged && currentAbility.getPhase() == AbilityPhase.IDLE) {
|
||||
handleAbilityCompletion(target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire extender tick hook (e.g., per-tick resource drain)
|
||||
if (!AbilityController.Instance.fireOnAbilityTick(currentAbility, entity, target,
|
||||
|
||||
@@ -317,6 +317,9 @@ public class CustomNpcs {
|
||||
new AddonManager();
|
||||
new AttributeController();
|
||||
new MagicController();
|
||||
|
||||
SyncController.register();
|
||||
|
||||
|
||||
APIRegistry.Instance.register("CNPC+ API", "https://kamkeel.github.io/CustomNPC-Plus/");
|
||||
}
|
||||
|
||||
@@ -188,29 +188,34 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
if (maxCooldown > minCooldown) {
|
||||
baseCooldown = minCooldown + random.nextInt(maxCooldown - minCooldown + 1);
|
||||
}
|
||||
cooldownEndTime = npc.worldObj.getTotalWorldTime() + baseCooldown + ability.getCooldownTicks();
|
||||
long now = npc.worldObj.getTotalWorldTime();
|
||||
|
||||
// Additionally set per-ability cooldown if enabled
|
||||
if (ability.isPerAbilityCooldown() && ability.getCooldownTicks() > 0) {
|
||||
long endTime = npc.worldObj.getTotalWorldTime() + ability.getCooldownTicks();
|
||||
setPerAbilityCooldown(ability.getName(), endTime, ability.getCooldownTicks());
|
||||
// The ability's own cooldown is tracked against the ability, so the global timer
|
||||
// only paces how often the NPC acts. Folding the ability's cooldown into it as
|
||||
// well would always outlast the per-ability entry and make it unreachable.
|
||||
cooldownEndTime = now + baseCooldown;
|
||||
setPerAbilityCooldown(ability.getName(), now + ability.getCooldownTicks(), ability.getCooldownTicks());
|
||||
} else {
|
||||
cooldownEndTime = now + baseCooldown + ability.getCooldownTicks();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void rollChainCooldown(ChainedAbility chain) {
|
||||
long now = npc.worldObj.getTotalWorldTime();
|
||||
boolean perAbility = chain.isPerAbilityCooldown() && chain.getCooldownTicks() > 0;
|
||||
|
||||
if (!chain.isIgnoreCooldown()) {
|
||||
int baseCooldown = minCooldown;
|
||||
if (maxCooldown > minCooldown) {
|
||||
baseCooldown = minCooldown + random.nextInt(maxCooldown - minCooldown + 1);
|
||||
}
|
||||
cooldownEndTime = npc.worldObj.getTotalWorldTime() + baseCooldown + chain.getCooldownTicks();
|
||||
cooldownEndTime = perAbility ? now + baseCooldown : now + baseCooldown + chain.getCooldownTicks();
|
||||
}
|
||||
|
||||
// Additionally set per-ability cooldown if enabled
|
||||
if (chain.isPerAbilityCooldown() && chain.getCooldownTicks() > 0) {
|
||||
long endTime = npc.worldObj.getTotalWorldTime() + chain.getCooldownTicks();
|
||||
setPerAbilityCooldown(chain.getName(), endTime, chain.getCooldownTicks());
|
||||
if (perAbility) {
|
||||
setPerAbilityCooldown(chain.getName(), now + chain.getCooldownTicks(), chain.getCooldownTicks());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -640,7 +640,7 @@ public class NoppesUtilServer {
|
||||
playerdata.save();
|
||||
}
|
||||
if (pl != null) {
|
||||
SyncController.syncPlayer((EntityPlayerMP) pl);
|
||||
SyncController.dispatchSyncForAllTypes((EntityPlayerMP) pl);
|
||||
}
|
||||
sendPlayerData(type, player, name);
|
||||
}
|
||||
@@ -1229,7 +1229,7 @@ public class NoppesUtilServer {
|
||||
playerdata.save();
|
||||
|
||||
if (pl != null) {
|
||||
SyncController.syncPlayer((EntityPlayerMP) pl);
|
||||
SyncController.dispatchSyncForAllTypes((EntityPlayerMP) pl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1261,7 +1261,7 @@ public class NoppesUtilServer {
|
||||
|
||||
playerdata.save();
|
||||
if (pl != null) {
|
||||
SyncController.syncPlayer((EntityPlayerMP) pl);
|
||||
SyncController.dispatchSyncForAllTypes((EntityPlayerMP) pl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import kamkeel.npcs.addon.DBCAddon;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.AttributeController;
|
||||
import kamkeel.npcs.controllers.data.attribute.tracker.PlayerAttributeTracker;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerAbilitySyncHelper;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerDataSyncHandler;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerEffectSyncHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCounter;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityDefend;
|
||||
@@ -141,7 +143,7 @@ public class ScriptPlayerEventHandler {
|
||||
}
|
||||
|
||||
if (playerData.updateClient) {
|
||||
SyncController.syncPlayerData((EntityPlayerMP) player, true);
|
||||
PlayerDataSyncHandler.syncPlayerData((EntityPlayerMP) player, true);
|
||||
playerData.updateClient = false;
|
||||
}
|
||||
|
||||
@@ -151,8 +153,8 @@ public class ScriptPlayerEventHandler {
|
||||
|
||||
if (player.ticksExisted % 10 == 0) {
|
||||
EntityPlayerMP mp = (EntityPlayerMP) player;
|
||||
SyncController.syncEffects(mp);
|
||||
SyncController.syncAbilityCooldowns(mp);
|
||||
PlayerEffectSyncHelper.syncEffects(mp);
|
||||
PlayerAbilitySyncHelper.syncAbilityCooldowns(mp);
|
||||
PlayerAbilitySyncPacket.sendToPlayer(mp);
|
||||
playerData.abilityData.ensureLockStateClear();
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.handlers.CarpentryRecipeSyncHandler;
|
||||
import kamkeel.npcs.controllers.sync.handlers.WorkbenchRecipeSyncHandler;
|
||||
import kamkeel.npcs.controllers.data.energycharge.EnergyChargeTracker;
|
||||
import kamkeel.npcs.entity.EntityEnergyBarrier;
|
||||
import kamkeel.npcs.entity.EntityEnergyDome;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumSoundOperation;
|
||||
import kamkeel.npcs.network.enums.EnumSyncAction;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import kamkeel.npcs.network.packets.data.ClonerPacket;
|
||||
import kamkeel.npcs.network.packets.data.MarkDataPacket;
|
||||
import kamkeel.npcs.network.packets.data.SoundManagementPacket;
|
||||
@@ -255,20 +257,22 @@ public class ServerEventsHandler {
|
||||
|
||||
if (block == Blocks.crafting_table && event.action == Action.RIGHT_CLICK_BLOCK && !player.worldObj.isRemote) {
|
||||
PacketHandler.Instance.sendToPlayer(new SyncPacket(
|
||||
EnumSyncType.WORKBENCH_RECIPES,
|
||||
SyncType.WORKBENCH_RECIPES,
|
||||
EnumSyncAction.RELOAD,
|
||||
-1,
|
||||
SyncController.getCurrentRevision(EnumSyncType.WORKBENCH_RECIPES),
|
||||
SyncController.workbenchNBT()
|
||||
null,
|
||||
SyncController.getCurrentServerRevision(SyncType.WORKBENCH_RECIPES),
|
||||
WorkbenchRecipeSyncHandler.getInstance().serializeAll()
|
||||
), (EntityPlayerMP) player);
|
||||
}
|
||||
if (block == CustomItems.carpentyBench && event.action == Action.RIGHT_CLICK_BLOCK && !player.worldObj.isRemote) {
|
||||
PacketHandler.Instance.sendToPlayer(new SyncPacket(
|
||||
EnumSyncType.CARPENTRY_RECIPES,
|
||||
SyncType.CARPENTRY_RECIPES,
|
||||
EnumSyncAction.RELOAD,
|
||||
-1,
|
||||
SyncController.getCurrentRevision(EnumSyncType.CARPENTRY_RECIPES),
|
||||
SyncController.carpentryNBT()
|
||||
null,
|
||||
SyncController.getCurrentServerRevision(SyncType.CARPENTRY_RECIPES),
|
||||
CarpentryRecipeSyncHandler.getInstance().serializeAll()
|
||||
), (EntityPlayerMP) player);
|
||||
}
|
||||
if ((block == CustomItems.banner || block == CustomItems.wallBanner || block == CustomItems.sign) && event.action == Action.RIGHT_CLICK_BLOCK) {
|
||||
|
||||
@@ -6,6 +6,8 @@ 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.controllers.sync.handlers.PlayerAbilitySyncHelper;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerEffectSyncHelper;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import noppes.npcs.controllers.AuctionController;
|
||||
@@ -45,13 +47,12 @@ public class ServerTickHandler {
|
||||
playerData.onLogin();
|
||||
}
|
||||
|
||||
ProfileController.Instance.login(player);
|
||||
SyncController.beginLogin(player);
|
||||
SyncController.syncEffects(player);
|
||||
PlayerEffectSyncHelper.syncEffects(player);
|
||||
ScriptController.Instance.syncClientScripts(player);
|
||||
|
||||
// Sync clean ability state to the new client (after beginLogin initializes sync state)
|
||||
SyncController.syncAbilities(player);
|
||||
PlayerAbilitySyncHelper.syncAbilities(player);
|
||||
|
||||
// Send auction notifications on login
|
||||
if (AuctionController.Instance != null) {
|
||||
|
||||
@@ -10,7 +10,10 @@ public class EntityAITransform extends EntityAIBase {
|
||||
|
||||
public EntityAITransform(EntityNPCInterface npc) {
|
||||
this.npc = npc;
|
||||
setMutexBits(AiMutex.PASSIVE);
|
||||
// No mutex: this only polls the world time and flips the transform, it drives nothing.
|
||||
// Claiming PASSIVE let any running movement or look task starve it, so the day/night
|
||||
// swap only fired in the gaps where those yielded - such as while saying a line.
|
||||
setMutexBits(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,10 +67,6 @@ public class BlockBorder extends BlockContainer {
|
||||
}
|
||||
|
||||
tile.rotation = l;
|
||||
|
||||
if (par5EntityLivingBase instanceof EntityPlayer && par1World.isRemote) {
|
||||
CustomNpcs.proxy.openGui(x, y, z, EnumGuiType.Border, (EntityPlayer) par5EntityLivingBase);
|
||||
}
|
||||
}
|
||||
|
||||
private TileBorder getTile(World world, int x, int y, int z) {
|
||||
|
||||
@@ -49,13 +49,6 @@ public class BlockNpcRedstone extends BlockContainer {
|
||||
par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World world, int i, int j, int k, EntityLivingBase entityliving, ItemStack item) {
|
||||
if (entityliving instanceof EntityPlayer && world.isRemote) {
|
||||
CustomNpcs.proxy.openGui(i, j, k, EnumGuiType.RedstoneBlock, (EntityPlayer) entityliving);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5) {
|
||||
onBlockAdded(par1World, par2, par3, par4);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package noppes.npcs.client;
|
||||
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import noppes.npcs.client.controllers.MusicController;
|
||||
@@ -18,8 +18,8 @@ import noppes.npcs.controllers.data.PlayerData;
|
||||
import noppes.npcs.controllers.data.SkinOverlay;
|
||||
import noppes.npcs.util.CacheHashMap;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -32,14 +32,14 @@ public class ClientCacheHandler {
|
||||
|
||||
private static String activeServerKey = "";
|
||||
private static String lastServerKey = "";
|
||||
private static final Map<String, EnumMap<EnumSyncType, Integer>> clientRevisionCache = new HashMap<>();
|
||||
private static final Map<String, Map<SyncType, Integer>> clientRevisionCache = new HashMap<>();
|
||||
|
||||
public static Party party;
|
||||
|
||||
public static boolean allowProfiles = true;
|
||||
public static boolean allowParties = true;
|
||||
|
||||
public static void setActiveServer(String serverKey, EnumMap<EnumSyncType, Integer> serverRevisions) {
|
||||
public static void setActiveServer(String serverKey, Map<SyncType, Integer> serverRevisions) {
|
||||
String normalizedKey = serverKey == null ? "" : serverKey;
|
||||
String previousKey = activeServerKey == null ? "" : activeServerKey;
|
||||
lastServerKey = previousKey;
|
||||
@@ -54,9 +54,9 @@ public class ClientCacheHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
EnumMap<EnumSyncType, Integer> cached = clientRevisionCache.computeIfAbsent(
|
||||
Map<SyncType, Integer> cached = clientRevisionCache.computeIfAbsent(
|
||||
activeServerKey,
|
||||
ignored -> new EnumMap<>(EnumSyncType.class)
|
||||
ignored -> new LinkedHashMap<>()
|
||||
);
|
||||
if (serverRevisions != null && !serverRevisions.isEmpty()) {
|
||||
cached.keySet().retainAll(serverRevisions.keySet());
|
||||
@@ -67,19 +67,19 @@ public class ClientCacheHandler {
|
||||
return lastServerKey == null ? "" : lastServerKey;
|
||||
}
|
||||
|
||||
public static EnumMap<EnumSyncType, Integer> getCachedRevisionsForServer(String serverKey) {
|
||||
public static Map<SyncType, Integer> getCachedRevisionsForServer(String serverKey) {
|
||||
String key = serverKey == null ? "" : serverKey;
|
||||
if (key.isEmpty()) {
|
||||
return new EnumMap<>(EnumSyncType.class);
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
EnumMap<EnumSyncType, Integer> revisions = clientRevisionCache.get(key);
|
||||
Map<SyncType, Integer> revisions = clientRevisionCache.get(key);
|
||||
if (revisions == null) {
|
||||
return new EnumMap<>(EnumSyncType.class);
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
return new EnumMap<>(revisions);
|
||||
return new LinkedHashMap<>(revisions);
|
||||
}
|
||||
|
||||
public static void updateClientRevision(EnumSyncType type, int revision) {
|
||||
public static void updateClientRevision(SyncType type, int revision) {
|
||||
if (revision < 0) {
|
||||
return;
|
||||
}
|
||||
@@ -87,9 +87,9 @@ public class ClientCacheHandler {
|
||||
if (key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
EnumMap<EnumSyncType, Integer> revisions = clientRevisionCache.computeIfAbsent(
|
||||
Map<SyncType, Integer> revisions = clientRevisionCache.computeIfAbsent(
|
||||
key,
|
||||
ignored -> new EnumMap<>(EnumSyncType.class)
|
||||
ignored -> new LinkedHashMap<>()
|
||||
);
|
||||
revisions.put(type, revision);
|
||||
}
|
||||
@@ -126,14 +126,11 @@ public class ClientCacheHandler {
|
||||
ClientCacheHandler.playerAnimations.clear();
|
||||
ProfileClientConfig.reset();
|
||||
|
||||
// Clear Texture Caches
|
||||
GuiSoundSelection.cachedDomains.clear();
|
||||
GuiTextureSelection.cachedTextures.clear();
|
||||
|
||||
// Clear music/bard sounds
|
||||
MusicController.Instance.stopAllSounds();
|
||||
|
||||
// Clear Quest Tracker
|
||||
HudComponent component = ClientHudManager.getInstance().getHudComponents().get(EnumHudComponent.QuestTracker);
|
||||
if (component != null) {
|
||||
component.loadData(new NBTTagCompound());
|
||||
@@ -146,7 +143,6 @@ public class ClientCacheHandler {
|
||||
ClientCacheHandler.customOverlays.clear();
|
||||
ClientCacheHandler.skinOverlays.clear();
|
||||
|
||||
// Clear Texture Caches
|
||||
GuiSoundSelection.cachedDomains.clear();
|
||||
GuiTextureSelection.cachedTextures.clear();
|
||||
}
|
||||
|
||||
@@ -112,19 +112,19 @@ public class SubGuiNpcAvailabilityDialog extends SubGuiInterface implements GuiS
|
||||
}
|
||||
if (button.id == 10) {
|
||||
slot = 1;
|
||||
setSubGui(new GuiDialogSelection(availabitily.questId));
|
||||
setSubGui(new GuiDialogSelection(availabitily.dialogId));
|
||||
}
|
||||
if (button.id == 11) {
|
||||
slot = 2;
|
||||
setSubGui(new GuiDialogSelection(availabitily.quest2Id));
|
||||
setSubGui(new GuiDialogSelection(availabitily.dialog2Id));
|
||||
}
|
||||
if (button.id == 12) {
|
||||
slot = 3;
|
||||
setSubGui(new GuiDialogSelection(availabitily.quest3Id));
|
||||
setSubGui(new GuiDialogSelection(availabitily.dialog3Id));
|
||||
}
|
||||
if (button.id == 13) {
|
||||
slot = 4;
|
||||
setSubGui(new GuiDialogSelection(availabitily.quest4Id));
|
||||
setSubGui(new GuiDialogSelection(availabitily.dialog4Id));
|
||||
}
|
||||
if (button.id == 20) {
|
||||
availabitily.dialogId = -1;
|
||||
|
||||
@@ -4,6 +4,7 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
|
||||
@@ -12,6 +13,25 @@ public class SubGuiTrapPresetSelector extends SubGuiInterface {
|
||||
|
||||
public String selectedPreset = null;
|
||||
|
||||
/**
|
||||
* Builds the trap editor's preset field.
|
||||
* <p>
|
||||
* This lives here rather than in AbilityTrap because the lambda below carries this class in
|
||||
* its method descriptor. AbilityTrap is a both-side class, and @SideOnly does not strip the
|
||||
* synthetic lambda methods it generates - so anything reflecting over its declared methods
|
||||
* on a server would resolve that descriptor and fail to find this client-only type.
|
||||
*/
|
||||
public static FieldDef createPresetField(java.util.function.Consumer<String> applyPreset) {
|
||||
return FieldDef.subGuiField("gui.applyPreset",
|
||||
SubGuiTrapPresetSelector::new,
|
||||
gui -> {
|
||||
SubGuiTrapPresetSelector selector = (SubGuiTrapPresetSelector) gui;
|
||||
if (selector.selectedPreset != null) {
|
||||
applyPreset.accept(selector.selectedPreset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final String[] PRESET_NAMES = {"HIDDEN", "VENOM", "EXPLOSIVE", "CURSED", "SHOCK", "SNARE"};
|
||||
private static final String[] PRESET_LABELS = {
|
||||
"ability.preset.hidden", "ability.preset.venom", "ability.preset.explosive",
|
||||
|
||||
@@ -4,6 +4,7 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
|
||||
@@ -12,6 +13,25 @@ public class SubGuiZonePresetSelector extends SubGuiInterface {
|
||||
|
||||
public String selectedPreset = null;
|
||||
|
||||
/**
|
||||
* Builds the zone editor's preset field.
|
||||
* <p>
|
||||
* This lives here rather than in AbilityZone because the lambda below carries this class in
|
||||
* its method descriptor. AbilityZone is a both-side class, and @SideOnly does not strip the
|
||||
* synthetic lambda methods it generates - so anything reflecting over its declared methods
|
||||
* on a server would resolve that descriptor and fail to find this client-only type.
|
||||
*/
|
||||
public static FieldDef createPresetField(java.util.function.Consumer<String> applyPreset) {
|
||||
return FieldDef.subGuiField("gui.applyPreset",
|
||||
SubGuiZonePresetSelector::new,
|
||||
gui -> {
|
||||
SubGuiZonePresetSelector selector = (SubGuiZonePresetSelector) gui;
|
||||
if (selector.selectedPreset != null) {
|
||||
applyPreset.accept(selector.selectedPreset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final String[] PRESET_NAMES = {"DEFAULT", "TOXIC", "INFERNO", "ARCANE", "ELECTRIC", "FROST"};
|
||||
private static final String[] PRESET_LABELS = {
|
||||
"ability.preset.default", "ability.preset.toxic", "ability.preset.inferno",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package noppes.npcs.client.gui.advanced;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.GuiCustomScroll;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcLabel;
|
||||
@@ -11,6 +13,7 @@ import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
import noppes.npcs.controllers.MagicController;
|
||||
import noppes.npcs.controllers.data.Magic;
|
||||
import noppes.npcs.controllers.data.MagicData;
|
||||
import noppes.npcs.controllers.data.MagicEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -36,6 +39,30 @@ public class SubGuiAbilityMagic extends SubGuiInterface implements ICustomScroll
|
||||
|
||||
private GuiNpcTextField splitField;
|
||||
|
||||
/**
|
||||
* Builds the ability editor's magic field.
|
||||
* <p>
|
||||
* This lives here rather than in Ability because the lambdas below carry this class in
|
||||
* their method descriptors. Ability is a both-side class, and anything reflecting over its
|
||||
* declared methods on a server - Nashorn's adapter generator, for one - would resolve those
|
||||
* descriptors and fail to find this client-only type.
|
||||
*/
|
||||
public static FieldDef createMagicField(MagicData magicData) {
|
||||
return FieldDef.subGuiField("ability.magic.editor",
|
||||
() -> new SubGuiAbilityMagic(magicData.copy()),
|
||||
gui -> {
|
||||
MagicData result = ((SubGuiAbilityMagic) gui).magicData;
|
||||
magicData.getMagics().clear();
|
||||
for (Map.Entry<Integer, MagicEntry> e : result.getMagics().entrySet()) {
|
||||
magicData.getMagics().put(e.getKey(), e.getValue());
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> magicData.isEmpty()
|
||||
? StatCollector.translateToLocal("ability.magic.usesCaster")
|
||||
: magicData.getMagics().size() + " Magic(s)")
|
||||
.tab("General");
|
||||
}
|
||||
|
||||
public SubGuiAbilityMagic(MagicData data) {
|
||||
this.magicData = data;
|
||||
setBackground("menubg.png");
|
||||
|
||||
@@ -3,6 +3,8 @@ package noppes.npcs.client.gui.advanced;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHasEffect;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.GuiCustomScroll;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcTextField;
|
||||
@@ -25,6 +27,31 @@ import java.util.Map;
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class SubGuiCustomEffectSelect extends SubGuiInterface implements ICustomScrollListener, ITextfieldListener {
|
||||
|
||||
/**
|
||||
* Builds the condition editor's effect-select field.
|
||||
* <p>
|
||||
* This lives here rather than in ConditionHasEffect because the lambdas below carry this
|
||||
* class in their method descriptors. The condition is a both-side class, and @SideOnly does
|
||||
* not strip the synthetic lambda methods it generates - so anything reflecting over its
|
||||
* declared methods on a server would resolve those descriptors and fail to find this type.
|
||||
*/
|
||||
public static FieldDef createSelectField(ConditionHasEffect condition) {
|
||||
return FieldDef.subGuiField("condition.select_effect",
|
||||
() -> new SubGuiCustomEffectSelect(condition.getEffectId(), condition.getEffectIndex()),
|
||||
gui -> {
|
||||
SubGuiCustomEffectSelect sel = (SubGuiCustomEffectSelect) gui;
|
||||
if (sel.getSelectedEffectId() >= 0) {
|
||||
condition.setEffect(sel.getSelectedEffectId(), sel.getSelectedIndex());
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> {
|
||||
if (condition.getEffectId() < 0) return "None";
|
||||
CustomEffect effect = condition.getEffect();
|
||||
return effect != null ? effect.getName() : "ID:" + condition.getEffectId();
|
||||
})
|
||||
.clearable(() -> condition.setEffect(-1, 0));
|
||||
}
|
||||
|
||||
private GuiCustomScroll scroll;
|
||||
private final HashMap<String, Integer> displayNameToId = new HashMap<>();
|
||||
private final HashMap<String, Integer> allDisplayNameToId = new HashMap<>();
|
||||
|
||||
@@ -39,6 +39,7 @@ public class QuestTrackingComponent extends HudComponent {
|
||||
if (turnIn != null && !turnIn.isEmpty()) {
|
||||
turnInLines.add(convertColorCodes(turnIn));
|
||||
}
|
||||
updateOverlayHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,6 +240,19 @@ public class QuestTrackingComponent extends HudComponent {
|
||||
}
|
||||
|
||||
// ---------- Helper rendering methods ----------- (methods unchanged) ...
|
||||
private void updateOverlayHeight() {
|
||||
int lineHeight = mc.fontRenderer.FONT_HEIGHT + 4;
|
||||
int contentHeight = 5;
|
||||
contentHeight += questTitleLines.size() * lineHeight + 8;
|
||||
contentHeight += questCategoryLines.size() * lineHeight + 8;
|
||||
contentHeight += objectiveLines.size() * lineHeight;
|
||||
if (!turnInLines.isEmpty()) {
|
||||
contentHeight += 8;
|
||||
}
|
||||
contentHeight += turnInLines.size() * lineHeight;
|
||||
overlayHeight = contentHeight;
|
||||
}
|
||||
|
||||
private int renderTextBlock(List<String> lines, int startY, int align, int color) {
|
||||
int y = startY;
|
||||
FontRenderer font = mc.fontRenderer;
|
||||
|
||||
@@ -429,6 +429,9 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC
|
||||
}
|
||||
|
||||
private boolean isMouseOverScrollbar(int mouseX, int mouseY) {
|
||||
if (maxScrollY <= 0)
|
||||
return false;
|
||||
|
||||
int scrollbarX = guiLeft + xSize + 30;
|
||||
int scrollbarY = guiTop + 27;
|
||||
int scrollbarHeight = ySize - 35;
|
||||
|
||||
@@ -21,6 +21,7 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex
|
||||
|
||||
private final ResourceLocation slot = new ResourceLocation("customnpcs", "textures/gui/slot.png");
|
||||
private RoleTrader role;
|
||||
private boolean marketRelinked = false;
|
||||
|
||||
// Currency cost textfield IDs start at 100 (100-117 for slots 0-17)
|
||||
private static final int CURRENCY_FIELD_ID_START = 100;
|
||||
@@ -116,8 +117,14 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
PacketClient.sendClient(new TraderMarketSavePacket(role.marketName, false));
|
||||
// Linking to a different market already replaced the server's role with that market's
|
||||
// data. Pushing this GUI's pre-link state back would overwrite the market being linked to.
|
||||
if (marketRelinked)
|
||||
return;
|
||||
|
||||
// Save the role first so the market is written from the edited data, not the stale data.
|
||||
PacketClient.sendClient(new RoleSavePacket(role.writeToNBT(new NBTTagCompound())));
|
||||
PacketClient.sendClient(new TraderMarketSavePacket(role.marketName, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,6 +136,7 @@ public class GuiNpcTraderSetup extends GuiContainerNPCInterface2 implements ITex
|
||||
String name = guiNpcTextField.getText();
|
||||
if (!name.equalsIgnoreCase(role.marketName)) {
|
||||
role.marketName = name;
|
||||
marketRelinked = true;
|
||||
PacketClient.sendClient(new TraderMarketSavePacket(role.marketName, true));
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,8 @@ package noppes.npcs.client.gui.select;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionQuestCompleted;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.GuiCustomScroll;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcLabel;
|
||||
@@ -27,6 +29,31 @@ public class GuiQuestSelection extends SubGuiInterface implements ICustomScrollL
|
||||
private QuestCategory selectedCategory;
|
||||
public Quest selectedQuest;
|
||||
|
||||
/**
|
||||
* Builds the condition editor's quest-select field.
|
||||
* <p>
|
||||
* This lives here rather than in ConditionQuestCompleted because the lambdas below carry
|
||||
* this class in their method descriptors. The condition is a both-side class, and @SideOnly
|
||||
* does not strip the synthetic lambda methods it generates - so anything reflecting over its
|
||||
* declared methods on a server would resolve those descriptors and fail to find this type.
|
||||
*/
|
||||
public static FieldDef createConditionField(ConditionQuestCompleted condition) {
|
||||
return FieldDef.subGuiField("condition.select_quest",
|
||||
() -> new GuiQuestSelection(condition.getQuestId()),
|
||||
gui -> {
|
||||
GuiQuestSelection sel = (GuiQuestSelection) gui;
|
||||
if (sel.selectedQuest != null) {
|
||||
condition.setQuestId(sel.selectedQuest.id);
|
||||
}
|
||||
})
|
||||
.buttonLabel(() -> {
|
||||
if (condition.getQuestId() < 0) return "None";
|
||||
Quest q = QuestController.Instance.quests.get(condition.getQuestId());
|
||||
return q != null ? q.title : "ID:" + condition.getQuestId();
|
||||
})
|
||||
.clearable(() -> condition.setQuestId(-1));
|
||||
}
|
||||
|
||||
private GuiSelectionListener listener;
|
||||
private String catSearch = "";
|
||||
private String questSearch = "";
|
||||
|
||||
@@ -515,15 +515,28 @@ public abstract class GuiNPCInterface extends GuiScreen {
|
||||
}
|
||||
|
||||
boolean subGui = hasSubGui();
|
||||
|
||||
// Mouse.getDWheel() clears the accumulated delta, so only the first caller in a frame
|
||||
// ever sees it. Read it once here and let each widget take it in turn; anything that
|
||||
// consumes it zeroes it so later widgets - the script editor, for one - are not left
|
||||
// reading an already-drained wheel.
|
||||
mouseScroll = Mouse.getDWheel();
|
||||
|
||||
drawCenteredString(fontRendererObj, title, width / 2, guiTop + 4, 0xffffff);
|
||||
for (GuiNpcLabel label : labels.values())
|
||||
label.drawLabel(this, fontRendererObj);
|
||||
for (GuiCustomScroll scroll : scrolls.values()) {
|
||||
scroll.updateSubGUI(subGui);
|
||||
scroll.drawScreen(drawX, drawY, f, !subGui && scroll.isMouseOver(drawX, drawY) ? Mouse.getDWheel() : 0);
|
||||
int delta = !subGui && scroll.isMouseOver(drawX, drawY) ? mouseScroll : 0;
|
||||
if (delta != 0)
|
||||
mouseScroll = 0;
|
||||
scroll.drawScreen(drawX, drawY, f, delta);
|
||||
}
|
||||
for (GuiScrollWindow guiScrollableComponent : scrollWindows.values()) {
|
||||
guiScrollableComponent.drawScreen(drawX, drawY, f, !subGui && guiScrollableComponent.isMouseOver(drawX, drawY) ? Mouse.getDWheel() : 0);
|
||||
int delta = !subGui && guiScrollableComponent.isMouseOver(drawX, drawY) ? mouseScroll : 0;
|
||||
if (delta != 0)
|
||||
mouseScroll = 0;
|
||||
guiScrollableComponent.drawScreen(drawX, drawY, f, delta);
|
||||
}
|
||||
for (GuiDiagram diagram : diagrams.values()) {
|
||||
diagram.drawDiagram(drawX, drawY, subGui);
|
||||
|
||||
@@ -624,9 +624,11 @@ public class GuiScriptTextArea extends GuiNpcTextField {
|
||||
int maxScroll = Math.max(0, getPaddedLineCount() - container.visibleLines);
|
||||
|
||||
// Handle mouse wheel scroll
|
||||
int wheelDelta = ((GuiNPCInterface) listener).mouseScroll = Mouse.getDWheel();
|
||||
// Take whatever the parent screen has left for this frame rather than polling the wheel
|
||||
// again - the poll is destructive and the parent has already drained it.
|
||||
int wheelDelta = listener instanceof GuiNPCInterface ? ((GuiNPCInterface) listener).mouseScroll : Mouse.getDWheel();
|
||||
if (listener instanceof GuiNPCInterface) {
|
||||
((GuiNPCInterface) listener).mouseScroll = wheelDelta;
|
||||
((GuiNPCInterface) listener).mouseScroll = 0;
|
||||
|
||||
// Intercept wheel scroll for tooltip (highest priority)
|
||||
// Only consume the wheel event when the tooltip actually has content to scroll;
|
||||
|
||||
@@ -486,9 +486,11 @@ public class GuiScriptTextArea1 extends GuiNpcTextField {
|
||||
int maxScroll = Math.max(0, getPaddedLineCount() - container.visibleLines);
|
||||
|
||||
// Handle mouse wheel scroll
|
||||
int wheelDelta = ((GuiNPCInterface) listener).mouseScroll = Mouse.getDWheel();
|
||||
// Take whatever the parent screen has left for this frame rather than polling the wheel
|
||||
// again - the poll is destructive and the parent has already drained it.
|
||||
int wheelDelta = listener instanceof GuiNPCInterface ? ((GuiNPCInterface) listener).mouseScroll : Mouse.getDWheel();
|
||||
if (listener instanceof GuiNPCInterface) {
|
||||
((GuiNPCInterface) listener).mouseScroll = wheelDelta;
|
||||
((GuiNPCInterface) listener).mouseScroll = 0;
|
||||
boolean canScroll = !KEYS_OVERLAY.isVisible() || KEYS_OVERLAY.isVisible() && !KEYS_OVERLAY.aboveOverlay;
|
||||
if (wheelDelta != 0 && canScroll)
|
||||
scroll.applyWheelScroll(wheelDelta, maxScroll);
|
||||
|
||||
@@ -1,7 +1,108 @@
|
||||
package noppes.npcs.client.gui.util;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.SyncHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
/**
|
||||
* OVERHAULED IGuiData FRAMEWORK!
|
||||
* Based on the new {@link SyncController} and {@link SyncHandler} infrastructure
|
||||
*/
|
||||
public interface IGuiData {
|
||||
public void setGuiData(NBTTagCompound compound);
|
||||
|
||||
/**
|
||||
* Legacy data setter
|
||||
* @Deprecated Use {@link #onDataUpdated(NBTTagCompound)} or {@link #onDataRemoved(String)} instead
|
||||
*/
|
||||
default void setGuiData(NBTTagCompound compound) {
|
||||
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// USE THESE !!!
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** NOTE:
|
||||
* No need for full object dataset onDataReload on GUI open, as these datasets are
|
||||
* always freshly synced to their client's controllers thanks to {@link SyncController}
|
||||
*/
|
||||
|
||||
/**
|
||||
* When a controller's object gets saved on server side, the updated object is synced to the client
|
||||
* @param compound the compound of the updated object
|
||||
*/
|
||||
default void onDataUpdated(NBTTagCompound compound) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When a controller's object gets saved on server side, the updated object is synced to the client
|
||||
* @param key key of updated object
|
||||
* @param compound compound of updated object
|
||||
*/
|
||||
default void onDataUpdated(NBTTagCompound compound, String key) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When a controller's object gets saved on server side, the updated object is synced to the client
|
||||
* @param id id of updated object
|
||||
* @param compound compound of updated object
|
||||
*/
|
||||
default void onDataUpdated(NBTTagCompound compound, int id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When a controller's object gets deleted on server side, the deleted object is removed from the client
|
||||
* @param key key of removed object
|
||||
*/
|
||||
default void onDataRemoved(String key) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When a controller's object gets deleted on server side, the deleted object is removed from the client
|
||||
* @param id id of removed object
|
||||
*/
|
||||
default void onDataRemoved(int id) {
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Static dispatchers
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
static void legacySetGuiData(NBTTagCompound nbt) {
|
||||
GuiScreen gui = activeGui();
|
||||
if (gui instanceof IGuiData)
|
||||
((IGuiData) gui).setGuiData(nbt);
|
||||
}
|
||||
|
||||
static void notifyUpdate(NBTTagCompound nbt, String key, int id) {
|
||||
GuiScreen gui = activeGui();
|
||||
if (gui instanceof IGuiData) {
|
||||
((IGuiData) gui).onDataUpdated(nbt);
|
||||
((IGuiData) gui).onDataUpdated(nbt, key);
|
||||
((IGuiData) gui).onDataUpdated(nbt, id);
|
||||
}
|
||||
}
|
||||
|
||||
static void notifyRemove(String key, int id) {
|
||||
GuiScreen gui = activeGui();
|
||||
if (gui instanceof IGuiData) {
|
||||
((IGuiData) gui).onDataRemoved(key);
|
||||
((IGuiData) gui).onDataRemoved(id);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// Active Gui fetcher
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
static GuiScreen activeGui() {
|
||||
GuiScreen gui = Minecraft.getMinecraft().currentScreen;
|
||||
if (gui instanceof GuiNPCInterface && ((GuiNPCInterface) gui).hasSubGui())
|
||||
return ((GuiNPCInterface) gui).getSubGui();
|
||||
if (gui instanceof GuiContainerNPCInterface && ((GuiContainerNPCInterface) gui).hasSubGui())
|
||||
return ((GuiContainerNPCInterface) gui).getSubGui();
|
||||
return gui;
|
||||
}
|
||||
}
|
||||
@@ -325,78 +325,94 @@ public class TypeInfo {
|
||||
* @return A TypeInfo preserving the generic structure, or null if type is null
|
||||
*/
|
||||
public static TypeInfo fromGenericType(Type type) {
|
||||
return fromGenericType(type, new HashSet<TypeVariable<?>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolving type variables whose bounds are currently being expanded, so a
|
||||
* self-referential bound such as {@code E extends Enum<E>} terminates
|
||||
*/
|
||||
private static TypeInfo fromGenericType(Type type, Set<TypeVariable<?>> resolving) {
|
||||
if (type == null) return null;
|
||||
|
||||
|
||||
if (type instanceof Class<?>) {
|
||||
// Plain class - no generic info
|
||||
return fromClass((Class<?>) type);
|
||||
}
|
||||
|
||||
|
||||
if (type instanceof ParameterizedType) {
|
||||
// Generic type like List<String>
|
||||
ParameterizedType paramType = (ParameterizedType) type;
|
||||
Type rawType = paramType.getRawType();
|
||||
|
||||
|
||||
if (!(rawType instanceof Class<?>)) {
|
||||
// Edge case - should not happen normally
|
||||
return rawType != null ? fromGenericType(rawType) : null;
|
||||
return rawType != null ? fromGenericType(rawType, resolving) : null;
|
||||
}
|
||||
|
||||
|
||||
// Get the raw type as TypeInfo
|
||||
TypeInfo rawTypeInfo = fromClass((Class<?>) rawType);
|
||||
|
||||
|
||||
// Recursively convert type arguments
|
||||
Type[] typeArgs = paramType.getActualTypeArguments();
|
||||
List<TypeInfo> appliedArgs = new ArrayList<>(typeArgs.length);
|
||||
for (Type arg : typeArgs) {
|
||||
TypeInfo argInfo = fromGenericType(arg);
|
||||
TypeInfo argInfo = fromGenericType(arg, resolving);
|
||||
if (argInfo != null) {
|
||||
appliedArgs.add(argInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return a parameterized TypeInfo
|
||||
if (!appliedArgs.isEmpty()) {
|
||||
return rawTypeInfo.parameterize(appliedArgs);
|
||||
}
|
||||
return rawTypeInfo;
|
||||
}
|
||||
|
||||
|
||||
if (type instanceof GenericArrayType) {
|
||||
// Array of generic type, e.g. T[] or List<String>[]
|
||||
GenericArrayType arrayType = (GenericArrayType) type;
|
||||
TypeInfo elementType = fromGenericType(arrayType.getGenericComponentType());
|
||||
TypeInfo elementType = fromGenericType(arrayType.getGenericComponentType(), resolving);
|
||||
if (elementType != null) {
|
||||
return arrayOf(elementType);
|
||||
}
|
||||
return fromClass(Object[].class);
|
||||
}
|
||||
|
||||
|
||||
if (type instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVar = (TypeVariable<?>) type;
|
||||
String varName = typeVar.getName();
|
||||
// Check for bounds - if bounded, create typeParameter with bound
|
||||
Type[] bounds = typeVar.getBounds();
|
||||
if (bounds != null && bounds.length > 0 && bounds[0] != Object.class) {
|
||||
return TypeInfo.typeParameter(varName, fromGenericType(bounds[0]));
|
||||
// Already expanding this variable - emit it unbounded rather than recursing forever
|
||||
if (!resolving.add(typeVar)) {
|
||||
return TypeInfo.typeParameter(varName);
|
||||
}
|
||||
try {
|
||||
// Check for bounds - if bounded, create typeParameter with bound
|
||||
Type[] bounds = typeVar.getBounds();
|
||||
if (bounds != null && bounds.length > 0 && bounds[0] != Object.class) {
|
||||
return TypeInfo.typeParameter(varName, fromGenericType(bounds[0], resolving));
|
||||
}
|
||||
return TypeInfo.typeParameter(varName);
|
||||
} finally {
|
||||
resolving.remove(typeVar);
|
||||
}
|
||||
return TypeInfo.typeParameter(varName);
|
||||
}
|
||||
|
||||
|
||||
if (type instanceof WildcardType) {
|
||||
// Wildcard like ? or ? extends Number or ? super Integer
|
||||
WildcardType wildcard = (WildcardType) type;
|
||||
Type[] upperBounds = wildcard.getUpperBounds();
|
||||
|
||||
|
||||
// Use the upper bound if available (most useful for ? extends T)
|
||||
if (upperBounds.length > 0 && upperBounds[0] != Object.class) {
|
||||
return fromGenericType(upperBounds[0]);
|
||||
return fromGenericType(upperBounds[0], resolving);
|
||||
}
|
||||
|
||||
|
||||
// For ? super T or plain ?, just use Object
|
||||
return fromClass(Object.class);
|
||||
}
|
||||
|
||||
|
||||
// Unknown type - shouldn't happen in practice
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,9 @@ public class ModelMonkeyTail extends ModelRenderer {
|
||||
|
||||
public ModelMonkeyTail(ModelBiped base) {
|
||||
super(base);
|
||||
float heightFactor = -9.0f;
|
||||
// Cancels ModelTail's rotationPointY of 11 so the boxes land where JBRA puts them
|
||||
// on players: plain tail at 10, wrapped tail at 0.
|
||||
float heightFactor = -11.0f;
|
||||
|
||||
(this.monkey_large = new ModelRenderer(base, 38, 54)).addBox(-2.0f, -2.0f, 0.0f, 4, 4, 6);
|
||||
this.setRotation(this.monkey_large, -0.5235988f, 0.0f, 0.0f);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -128,7 +128,7 @@ public class CustomEffectController implements ICustomEffectHandler {
|
||||
file.delete();
|
||||
}
|
||||
categoryManager.removeItem(id);
|
||||
SyncController.syncRemove(EnumSyncType.CUSTOM_EFFECTS, foundEffect.getID());
|
||||
SyncController.syncRemove(SyncType.CUSTOM_EFFECTS, foundEffect.getID());
|
||||
saveEffectLoadMap();
|
||||
}
|
||||
}
|
||||
@@ -700,7 +700,7 @@ public class CustomEffectController implements ICustomEffectHandler {
|
||||
file2.delete();
|
||||
file.renameTo(file2);
|
||||
nbtTagCompound.removeTag("ScriptData");
|
||||
SyncController.syncUpdate(EnumSyncType.CUSTOM_EFFECTS, -1, nbtTagCompound);
|
||||
SyncController.syncUpdate(SyncType.CUSTOM_EFFECTS, -1, nbtTagCompound);
|
||||
} catch (Exception e) {
|
||||
LogWriter.except(e);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.controllers.sync.handlers.DialogCategorySyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -239,7 +240,7 @@ public class DialogController implements IDialogHandler {
|
||||
dir.mkdirs();
|
||||
}
|
||||
categories.put(category.id, category);
|
||||
SyncController.syncUpdate(EnumSyncType.DIALOG_CATEGORY, -1, SyncController.updateDialogCat(category));
|
||||
SyncController.syncUpdate(SyncType.DIALOG_CATEGORY, -1, DialogCategorySyncHandler.serializeCategory(category));
|
||||
}
|
||||
|
||||
public void removeCategory(int category) {
|
||||
@@ -252,7 +253,7 @@ public class DialogController implements IDialogHandler {
|
||||
for (int dia : cat.dialogs.keySet())
|
||||
dialogs.remove(dia);
|
||||
categories.remove(category);
|
||||
SyncController.syncRemove(EnumSyncType.DIALOG_CATEGORY, category);
|
||||
SyncController.syncRemove(SyncType.DIALOG_CATEGORY, category);
|
||||
}
|
||||
|
||||
private boolean containsCategoryName(String name) {
|
||||
@@ -302,7 +303,7 @@ public class DialogController implements IDialogHandler {
|
||||
if (file2.exists())
|
||||
file2.delete();
|
||||
file.renameTo(file2);
|
||||
SyncController.syncUpdate(EnumSyncType.DIALOG, category.id, dialog.writeToNBT(new NBTTagCompound()));
|
||||
SyncController.syncUpdate(SyncType.DIALOG, category.id, dialog.writeToNBT(new NBTTagCompound()));
|
||||
} catch (Exception e) {
|
||||
LogWriter.except(e);
|
||||
}
|
||||
@@ -316,7 +317,7 @@ public class DialogController implements IDialogHandler {
|
||||
return;
|
||||
category.dialogs.remove(dialog.id);
|
||||
dialogs.remove(dialog.id);
|
||||
SyncController.syncRemove(EnumSyncType.DIALOG, dialog.id);
|
||||
SyncController.syncRemove(SyncType.DIALOG, dialog.id);
|
||||
}
|
||||
|
||||
private File getDir() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -159,7 +159,7 @@ public class FactionController implements IFactionHandler {
|
||||
|
||||
NBTTagCompound facCompound = new NBTTagCompound();
|
||||
faction.writeNBT(facCompound);
|
||||
SyncController.syncUpdate(EnumSyncType.FACTION, -1, facCompound);
|
||||
SyncController.syncUpdate(SyncType.FACTION, -1, facCompound);
|
||||
saveFactions();
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public class FactionController implements IFactionHandler {
|
||||
} else {
|
||||
this.saveFactions();
|
||||
faction.id = -1;
|
||||
SyncController.syncRemove(EnumSyncType.FACTION, id);
|
||||
SyncController.syncRemove(SyncType.FACTION, id);
|
||||
return faction;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -275,7 +275,7 @@ public class MagicController implements IMagicHandler {
|
||||
|
||||
NBTTagCompound magicCompound = new NBTTagCompound();
|
||||
mag.writeNBT(magicCompound);
|
||||
SyncController.syncUpdate(EnumSyncType.MAGIC, -1, magicCompound);
|
||||
SyncController.syncUpdate(SyncType.MAGIC, -1, magicCompound);
|
||||
|
||||
saveMagicData();
|
||||
}
|
||||
@@ -283,7 +283,7 @@ public class MagicController implements IMagicHandler {
|
||||
public void removeMagic(int magicID) {
|
||||
if (magics.containsKey(magicID)) {
|
||||
magics.remove(magicID);
|
||||
SyncController.syncRemove(EnumSyncType.MAGIC, magicID);
|
||||
SyncController.syncRemove(SyncType.MAGIC, magicID);
|
||||
saveMagicData();
|
||||
}
|
||||
}
|
||||
@@ -345,7 +345,7 @@ public class MagicController implements IMagicHandler {
|
||||
|
||||
NBTTagCompound cycleCompound = new NBTTagCompound();
|
||||
cycle.writeNBT(cycleCompound);
|
||||
SyncController.syncUpdate(EnumSyncType.MAGIC_CYCLE, -1, cycleCompound);
|
||||
SyncController.syncUpdate(SyncType.MAGIC_CYCLE, -1, cycleCompound);
|
||||
|
||||
saveMagicData();
|
||||
}
|
||||
@@ -353,7 +353,7 @@ public class MagicController implements IMagicHandler {
|
||||
public void removeCycle(int categoryId) {
|
||||
if (cycles.containsKey(categoryId)) {
|
||||
cycles.remove(categoryId);
|
||||
SyncController.syncRemove(EnumSyncType.MAGIC_CYCLE, categoryId);
|
||||
SyncController.syncRemove(SyncType.MAGIC_CYCLE, categoryId);
|
||||
saveMagicData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.EnumSyncType;
|
||||
import kamkeel.npcs.controllers.sync.handlers.QuestCategorySyncHandler;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -158,7 +159,7 @@ public class QuestController implements IQuestHandler {
|
||||
for (int dia : cat.quests.keySet())
|
||||
quests.remove(dia);
|
||||
categories.remove(category);
|
||||
SyncController.syncRemove(EnumSyncType.QUEST_CATEGORY, category);
|
||||
SyncController.syncRemove(SyncType.QUEST_CATEGORY, category);
|
||||
}
|
||||
|
||||
public void saveCategory(QuestCategory category) {
|
||||
@@ -188,7 +189,7 @@ public class QuestController implements IQuestHandler {
|
||||
dir.mkdirs();
|
||||
}
|
||||
categories.put(category.id, category);
|
||||
SyncController.syncUpdate(EnumSyncType.QUEST_CATEGORY, -1, SyncController.updateQuestCat(category));
|
||||
SyncController.syncUpdate(SyncType.QUEST_CATEGORY, -1, QuestCategorySyncHandler.serializeCategory(category));
|
||||
}
|
||||
|
||||
private boolean containsCategoryName(String name) {
|
||||
@@ -245,7 +246,7 @@ public class QuestController implements IQuestHandler {
|
||||
if (file2.exists())
|
||||
file2.delete();
|
||||
file.renameTo(file2);
|
||||
SyncController.syncUpdate(EnumSyncType.QUEST, category.id, quest.writeToNBT(new NBTTagCompound()));
|
||||
SyncController.syncUpdate(SyncType.QUEST, category.id, quest.writeToNBT(new NBTTagCompound()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package noppes.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.network.enums.SyncType;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
@@ -282,12 +283,12 @@ public class RecipeController implements IRecipeHandler {
|
||||
return null;
|
||||
RecipeCarpentry globalRecipe = globalRecipes.remove(recipe.id);
|
||||
if (globalRecipe != null)
|
||||
SyncController.syncAllWorkbenchRecipes();
|
||||
SyncController.syncAll(SyncType.WORKBENCH_RECIPES);
|
||||
|
||||
RecipeCarpentry carpentry = carpentryRecipes.remove(recipe.id);
|
||||
if (carpentry != null) {
|
||||
carpentryScripts.remove(recipe.id);
|
||||
SyncController.syncAllCarpentryRecipes();
|
||||
SyncController.syncAll(SyncType.CARPENTRY_RECIPES);
|
||||
}
|
||||
|
||||
saveCategories();
|
||||
@@ -302,7 +303,7 @@ public class RecipeController implements IRecipeHandler {
|
||||
|
||||
RecipeAnvil anvilRecipe = anvilRecipes.remove(recipe.id);
|
||||
if (anvilRecipe != null)
|
||||
SyncController.syncAllAnvilRecipes();
|
||||
SyncController.syncAll(SyncType.ANVIL_RECIPES);
|
||||
|
||||
saveCategories();
|
||||
return recipe;
|
||||
|
||||
@@ -56,11 +56,17 @@ public class JaninoScriptHandler<S extends JaninoScript<?>> extends ScriptHandle
|
||||
public void readFromNBT(NBTTagCompound compound) {
|
||||
// JaninoScriptable's helper generics assume JaninoScript<T>, but most of our scripts are JaninoScript<?>.
|
||||
// Keep it simple and safe: treat it as raw.
|
||||
script = (S) JaninoScript.readFromNBT(compound, (JaninoScript) script, (Supplier) factory);
|
||||
NBTTagCompound tag = compound.getCompoundTag("Script");
|
||||
enabled = tag.getBoolean("enabled");
|
||||
script = (S) JaninoScript.readFromNBT(tag, (JaninoScript) script, (Supplier) factory);
|
||||
}
|
||||
|
||||
public void writeToNBT(NBTTagCompound compound) {
|
||||
JaninoScript.writeToNBT(compound, (JaninoScript) script);
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setBoolean("enabled", enabled);
|
||||
JaninoScript.writeToNBT(tag, (JaninoScript) script);
|
||||
compound.setTag("Script", tag);
|
||||
|
||||
}
|
||||
|
||||
// -------------------- IScriptHandler --------------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package noppes.npcs.controllers.data;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.SyncController;
|
||||
import kamkeel.npcs.controllers.sync.handlers.PlayerAbilitySyncHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
@@ -496,23 +496,23 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
|
||||
/**
|
||||
* Full sync of all ability state to the client.
|
||||
* Delegates to {@link SyncController#syncAbilities(EntityPlayerMP)}.
|
||||
* Delegates to {@link PlayerAbilitySyncHelper#syncAbilities(EntityPlayerMP)}.
|
||||
*/
|
||||
public void syncToClient() {
|
||||
EntityPlayer player = playerData.player;
|
||||
if (player instanceof EntityPlayerMP) {
|
||||
SyncController.syncAbilities((EntityPlayerMP) player);
|
||||
PlayerAbilitySyncHelper.syncAbilities((EntityPlayerMP) player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight sync of cooldown state only to the client.
|
||||
* Delegates to {@link SyncController#syncAbilityCooldowns(EntityPlayerMP)}.
|
||||
* Delegates to {@link PlayerAbilitySyncHelper#syncAbilityCooldowns(EntityPlayerMP)}.
|
||||
*/
|
||||
public void syncCooldownToClient() {
|
||||
EntityPlayer player = playerData.player;
|
||||
if (player instanceof EntityPlayerMP) {
|
||||
SyncController.syncAbilityCooldowns((EntityPlayerMP) player);
|
||||
PlayerAbilitySyncHelper.syncAbilityCooldowns((EntityPlayerMP) player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,21 +773,21 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully reset ability state when changing dimensions.
|
||||
* Clears cooldowns so the player can immediately use abilities in the new dimension.
|
||||
* Fully reset transient ability execution state when changing dimensions.
|
||||
* Cooldowns are preserved so dimension changes cannot bypass them.
|
||||
* Keeps active toggles (dimension change shouldn't reset ongoing effects).
|
||||
*/
|
||||
public void resetOnDimensionChange() {
|
||||
trackingAbilityPosition = false;
|
||||
onEntityReconstructed(true, false);
|
||||
onEntityReconstructed(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset ability state when a large-distance teleport is detected during execution.
|
||||
* Same behavior as dimension change — silent cleanup, clear cooldowns, keep toggles.
|
||||
* Same behavior as dimension change: silent cleanup, preserve cooldowns, keep toggles.
|
||||
*/
|
||||
public void resetOnTeleport() {
|
||||
onEntityReconstructed(true, false);
|
||||
onEntityReconstructed(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -832,9 +832,8 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
currentTarget = null;
|
||||
lastAbilityActivationTime = -1;
|
||||
|
||||
// Clear cooldowns (fresh start after relog)
|
||||
cooldownEndTime = 0;
|
||||
resetAllPerAbilityCooldowns();
|
||||
// Cooldowns are absolute world-time deadlines and are restored from NBT, so relogging
|
||||
// must not clear them or it becomes a way to bypass them.
|
||||
interruptCooldownRolled = false;
|
||||
|
||||
// Reset sync tracking so next sync sends fresh state
|
||||
@@ -852,9 +851,10 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
if (currentAbility != null && currentAbility.isExecuting()) {
|
||||
interruptCurrentAbility(null, 0);
|
||||
}
|
||||
// Clear any remaining transient state (cooldowns, chains, locks)
|
||||
// Clear any remaining transient state (chains, locks)
|
||||
// Don't clear toggles — they were already saved to NBT by this point
|
||||
onEntityReconstructed(true, false);
|
||||
// Don't clear cooldowns — they persist across sessions
|
||||
onEntityReconstructed(false, false);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -1048,6 +1048,22 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
toggleList.appendTag(toggleNbt);
|
||||
}
|
||||
compound.setTag("ActiveToggles", toggleList);
|
||||
|
||||
// Cooldowns are absolute world-time deadlines, so they stay meaningful across sessions
|
||||
NBTTagCompound cooldowns = new NBTTagCompound();
|
||||
cooldowns.setLong("GlobalEnd", cooldownEndTime);
|
||||
cooldowns.setInteger("GlobalDuration", globalCooldownDuration);
|
||||
NBTTagList perAbilityList = new NBTTagList();
|
||||
for (Map.Entry<String, Long> entry : perAbilityCooldownEndTimes.entrySet()) {
|
||||
NBTTagCompound cooldownNbt = new NBTTagCompound();
|
||||
cooldownNbt.setString("Key", entry.getKey());
|
||||
cooldownNbt.setLong("End", entry.getValue());
|
||||
Integer duration = perAbilityCooldownDurations.get(entry.getKey());
|
||||
cooldownNbt.setInteger("Duration", duration == null ? 0 : duration);
|
||||
perAbilityList.appendTag(cooldownNbt);
|
||||
}
|
||||
cooldowns.setTag("PerAbility", perAbilityList);
|
||||
compound.setTag("AbilityCooldowns", cooldowns);
|
||||
}
|
||||
|
||||
public void readFromNBT(NBTTagCompound compound) {
|
||||
@@ -1082,6 +1098,24 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
setToggleEntryDirect(key, state);
|
||||
}
|
||||
}
|
||||
|
||||
if (compound.hasKey("AbilityCooldowns")) {
|
||||
cooldownEndTime = 0;
|
||||
globalCooldownDuration = 0;
|
||||
resetAllPerAbilityCooldowns();
|
||||
|
||||
NBTTagCompound cooldowns = compound.getCompoundTag("AbilityCooldowns");
|
||||
cooldownEndTime = cooldowns.getLong("GlobalEnd");
|
||||
globalCooldownDuration = cooldowns.getInteger("GlobalDuration");
|
||||
NBTTagList perAbilityList = cooldowns.getTagList("PerAbility", 10); // 10 = TAG_COMPOUND
|
||||
for (int i = 0; i < perAbilityList.tagCount(); i++) {
|
||||
NBTTagCompound entry = perAbilityList.getCompoundTagAt(i);
|
||||
String key = entry.getString("Key");
|
||||
if (key == null || key.isEmpty()) continue;
|
||||
perAbilityCooldownEndTimes.put(key, entry.getLong("End"));
|
||||
perAbilityCooldownDurations.put(key, entry.getInteger("Duration"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
public abstract class ScriptHandler implements IScriptHandler {
|
||||
public List<IScriptUnit> scripts = new ArrayList<>();
|
||||
protected String scriptLanguage = "ECMAScript";
|
||||
protected boolean enabled = false;
|
||||
protected boolean enabled = true;
|
||||
protected long lastInited = -1;
|
||||
|
||||
protected boolean canRunScripts() {
|
||||
|
||||
@@ -301,7 +301,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
|
||||
@Override
|
||||
public boolean getLeashed() {
|
||||
return false; //Prevents npcs from being leashed
|
||||
return false; // Prevents npcs from being leashed
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -544,15 +544,13 @@ public abstract class JaninoScript<T> implements IScriptUnit {
|
||||
}
|
||||
|
||||
public static <T, S extends JaninoScript<T>> S readFromNBT(NBTTagCompound compound, S script, Supplier<S> factory) {
|
||||
if (compound.hasKey("Script")) {
|
||||
if (script == null) script = factory.get();
|
||||
script.readFromNBT(compound.getCompoundTag("Script"));
|
||||
}
|
||||
if (script == null) script = factory.get();
|
||||
script.readFromNBT(compound);
|
||||
return script;
|
||||
}
|
||||
|
||||
public static <T> NBTTagCompound writeToNBT(NBTTagCompound compound, JaninoScript<T> script) {
|
||||
if (script != null) compound.setTag("Script", script.writeToNBT(new NBTTagCompound()));
|
||||
if (script != null) script.writeToNBT(compound);
|
||||
return compound;
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,15 @@ public class RoleTrader extends RoleInterface {
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound nbttagcompound) {
|
||||
readFromNBT(nbttagcompound, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loadFromMarket pull the linked market's stored data over the compound just read.
|
||||
* False when the compound is an admin edit, which is authoritative and
|
||||
* would otherwise be overwritten by the market it is about to be saved to.
|
||||
*/
|
||||
public void readFromNBT(NBTTagCompound nbttagcompound, boolean loadFromMarket) {
|
||||
String oldMarket = marketName;
|
||||
marketName = nbttagcompound.getString("TraderMarket");
|
||||
readNBT(nbttagcompound);
|
||||
@@ -101,6 +110,9 @@ public class RoleTrader extends RoleInterface {
|
||||
// Register with MarketRegistry for linked market sync
|
||||
MarketRegistry.updateTraderMarket(oldMarket, marketName, this);
|
||||
|
||||
if (!loadFromMarket)
|
||||
return;
|
||||
|
||||
try {
|
||||
Market.getMarket(this, marketName);
|
||||
} catch (Exception ex) {
|
||||
|
||||
Reference in new issue
Block a user