diff --git a/src/main/java/noppes/npcs/CommonProxy.java b/src/main/java/noppes/npcs/CommonProxy.java index 68c9db672..5bf78ad28 100644 --- a/src/main/java/noppes/npcs/CommonProxy.java +++ b/src/main/java/noppes/npcs/CommonProxy.java @@ -1,5 +1,6 @@ package noppes.npcs; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.network.IGuiHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -23,7 +24,6 @@ import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.util.MillisTimer; -import java.util.ConcurrentModificationException; import java.util.HashSet; public class CommonProxy implements IGuiHandler { @@ -42,34 +42,46 @@ public class CommonProxy implements IGuiHandler { protected void createAnimationThread() { Thread thread = (new Thread("Animation Thread") { public void run() { while (true) { - if (!CustomNpcs.proxy.hasClient() || !Minecraft.getMinecraft().isGamePaused()) { - animationTimer.updateTimer(); - } - - HashSet playingAnimations = CustomNpcs.proxy.hasClient() ? clientPlayingAnimations : serverPlayingAnimations; - for (int i = 0; i < animationTimer.elapsedTicks; ++i) { - for (Animation animation : playingAnimations) { - int tickDuration = animation.currentFrame().tickDuration(); - if (animation.parent.isActive() && totalTicks % tickDuration == 0) { - animation.increaseTime(); - } - } - totalTicks++; - } - totalTicks %= 60 * 60 * 1000; - try { - clientPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); - serverPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); - } catch (ConcurrentModificationException ignored) {} + if (!CustomNpcs.proxy.hasClient() || !Minecraft.getMinecraft().isGamePaused()) { + animationTimer.updateTimer(); + } + + for (int i = 0; i < animationTimer.elapsedTicks; ++i) { + updateAnimations(clientPlayingAnimations); + updateAnimations(serverPlayingAnimations); + totalTicks++; + } + totalTicks %= 60 * 60 * 1000; + + synchronized (clientPlayingAnimations) { + clientPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); + } + synchronized (serverPlayingAnimations) { + serverPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); + } + } catch (Exception ignored) { + //Must ignore exceptions in this thread so a single animation error doesn't break all animations. + } } }}); thread.setDaemon(true); thread.start(); } + private void updateAnimations(HashSet playingAnimations) { + for (Animation animation : playingAnimations) { + int tickDuration = animation.currentFrame().tickDuration(); + if ((animation.parent.isActive() + || animation.parent.animation == null && animation.parent.isActive(animation.parent.currentClientAnimation)) + && totalTicks % tickDuration == 0) { + animation.increaseTime(); + } + } + } + private boolean removeAnimation(Animation animation) { - if (animation.currentFrame() == null || animation.parent == null || animation.parent.animation != animation) { + if (animation.currentFrame() == null || animation.parent == null) { return true; } @@ -86,17 +98,19 @@ public class CommonProxy implements IGuiHandler { if (entity == null) return true; - if (CustomNpcs.proxy.hasClient()) { - return clientRemoveAnimation(entity); + if (entity.worldObj != null && entity.worldObj.isRemote) { + return animation.parent.animation != animation || clientRemoveAnimation(entity); } else { - return entity.worldObj == null || !entity.worldObj.loadedEntityList.contains(entity); + return animation.parent.animation != animation && animation.parent.currentClientAnimation != animation + || entity.worldObj == null || !entity.worldObj.loadedEntityList.contains(entity) + || CustomNpcs.getServer().isServerStopped(); } } @SideOnly(Side.CLIENT) private boolean clientRemoveAnimation(Entity entity) { - return Minecraft.getMinecraft().currentScreen == null - && !Minecraft.getMinecraft().theWorld.loadedEntityList.contains(entity); + return Minecraft.getMinecraft().theWorld == null + || Minecraft.getMinecraft().currentScreen == null && !Minecraft.getMinecraft().theWorld.loadedEntityList.contains(entity); } public PlayerData getPlayerData(EntityPlayer player) { diff --git a/src/main/java/noppes/npcs/ServerEventsHandler.java b/src/main/java/noppes/npcs/ServerEventsHandler.java index ab7b67a5d..7742199d8 100644 --- a/src/main/java/noppes/npcs/ServerEventsHandler.java +++ b/src/main/java/noppes/npcs/ServerEventsHandler.java @@ -497,12 +497,24 @@ public class ServerEventsHandler { @SubscribeEvent public void playerTracking(PlayerEvent.StartTracking event){ - if(!(event.target instanceof EntityNPCInterface) || event.target.worldObj.isRemote) - return; + if (event.target.worldObj.isRemote) return; - MarkData data = MarkData.get((EntityNPCInterface) event.target); - if(data.marks.isEmpty()) - return; - Server.sendData((EntityPlayerMP)event.entityPlayer, EnumPacketClient.MARK_DATA, event.target.getEntityId(), data.getNBT()); + if(event.target instanceof EntityPlayerMP || event.target instanceof EntityNPCInterface) { + AnimationData animationData = AnimationData.getData(event.target); + if (animationData.isClientAnimating()) { + AnimationData playerAnimData = AnimationData.getData(event.entityPlayer); + Animation currentAnimation = animationData.currentClientAnimation; + NBTTagCompound compound = currentAnimation.writeToNBT(); + playerAnimData.viewAnimation(currentAnimation, animationData, compound, + animationData.isClientAnimating(), currentAnimation.currentFrame, currentAnimation.currentFrameTime); + } + + if (event.target instanceof EntityNPCInterface) { + MarkData data = MarkData.get((EntityNPCInterface) event.target); + if (data.marks.isEmpty()) + return; + Server.sendData((EntityPlayerMP) event.entityPlayer, EnumPacketClient.MARK_DATA, event.target.getEntityId(), data.getNBT()); + } + } } } diff --git a/src/main/java/noppes/npcs/client/ClientCacheHandler.java b/src/main/java/noppes/npcs/client/ClientCacheHandler.java index bf9c920f4..810cc4bf0 100644 --- a/src/main/java/noppes/npcs/client/ClientCacheHandler.java +++ b/src/main/java/noppes/npcs/client/ClientCacheHandler.java @@ -57,7 +57,6 @@ public class ClientCacheHandler { ClientCacheHandler.skinOverlays.clear(); ClientCacheHandler.playerAnimations.clear(); ClientCacheHandler.playerData.animationData.clearCache(); - CommonProxy.clientPlayingAnimations.clear(); } public static void clearSkinCache() { diff --git a/src/main/java/noppes/npcs/client/ClientTickHandler.java b/src/main/java/noppes/npcs/client/ClientTickHandler.java index 4be801a9b..a2f82b27e 100644 --- a/src/main/java/noppes/npcs/client/ClientTickHandler.java +++ b/src/main/java/noppes/npcs/client/ClientTickHandler.java @@ -32,7 +32,7 @@ public class ClientTickHandler{ @SubscribeEvent(priority = EventPriority.LOWEST) public void onClientTick(TickEvent.ClientTickEvent event){ Minecraft mc = Minecraft.getMinecraft(); - if ((this.prevWorld == null || mc.theWorld == null) && this.prevWorld != mc.theWorld) { + if (mc.theWorld != null && this.prevWorld != mc.theWorld) { ClientCacheHandler.clearCache(); this.prevWorld = mc.theWorld; } diff --git a/src/main/java/noppes/npcs/client/PacketHandlerClient.java b/src/main/java/noppes/npcs/client/PacketHandlerClient.java index b989406cb..7c3ef866c 100644 --- a/src/main/java/noppes/npcs/client/PacketHandlerClient.java +++ b/src/main/java/noppes/npcs/client/PacketHandlerClient.java @@ -375,8 +375,14 @@ public class PacketHandlerClient extends PacketHandlerServer{ } animationData.setAnimation(ClientCacheHandler.animationCache.get(animationId)); - animationData.readFromNBT(compound); - Client.sendData(EnumPacketServer.CacheAnimation, animationId); + animationData.viewReadFromNBT(compound); + + if (compound.hasKey("Frame")) { + animationData.animation.readFromNBT(compound.getCompoundTag("Animation")); + animationData.animation.jumpToFrameAtTime(compound.getInteger("Frame"), compound.getInteger("Time")); + } else if (compound.hasKey("Animation")) { + Client.sendData(EnumPacketServer.CacheAnimation, animationId); + } } } else if(type == EnumPacketClient.DISABLE_MOUSE_INPUT) { diff --git a/src/main/java/noppes/npcs/controllers/data/Animation.java b/src/main/java/noppes/npcs/controllers/data/Animation.java index 0b9ae8fd7..80fc28400 100644 --- a/src/main/java/noppes/npcs/controllers/data/Animation.java +++ b/src/main/java/noppes/npcs/controllers/data/Animation.java @@ -5,6 +5,8 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import noppes.npcs.CustomNpcs; +import noppes.npcs.DataDisplay; import noppes.npcs.EventHooks; import noppes.npcs.api.handler.data.IAnimation; import noppes.npcs.api.handler.data.IAnimationData; @@ -210,6 +212,9 @@ public class Animation implements IAnimation { this.whileStanding = compound.getBoolean("WhileStanding"); this.whileMoving = compound.getBoolean("WhileWalking"); this.whileAttacking = compound.getBoolean("WhileAttacking"); + + this.currentFrame = compound.getInteger("CurrentFrame"); + this.currentFrameTime = compound.getInteger("CurrentFrameTime"); } public NBTTagCompound writeToNBT(){ @@ -231,6 +236,9 @@ public class Animation implements IAnimation { compound.setBoolean("WhileStanding", whileStanding); compound.setBoolean("WhileWalking", whileMoving); compound.setBoolean("WhileAttacking", whileAttacking); + + compound.setInteger("CurrentFrame", currentFrame); + compound.setInteger("CurrentFrameTime", currentFrameTime); return compound; } @@ -277,16 +285,23 @@ public class Animation implements IAnimation { @SideOnly(Side.CLIENT) public void jumpToCurrentFrame() { - this.currentFrameTime = 0; - Frame frame = (Frame) this.currentFrame(); - if(frame != null){ - for (EnumAnimationPart part : EnumAnimationPart.values()) { - if(part != null){ - if (frame.frameParts.containsKey(part)) { - frame.frameParts.get(part).jumpToCurrentFrame(); - } - } - } - } + this.jumpToFrameAtTime(this.currentFrame, 0); } + + @SideOnly(Side.CLIENT) + public void jumpToFrameAtTime(int frameIndex, int time) { + this.currentFrame = frameIndex; + this.currentFrameTime = time; + + Frame frame = (Frame) this.currentFrame(); + if(frame != null){ + for (EnumAnimationPart part : EnumAnimationPart.values()) { + if(part != null){ + if (frame.frameParts.containsKey(part)) { + frame.frameParts.get(part).jumpToCurrentFrame(); + } + } + } + } + } } diff --git a/src/main/java/noppes/npcs/controllers/data/AnimationData.java b/src/main/java/noppes/npcs/controllers/data/AnimationData.java index d8d4a07f7..5a4e25834 100644 --- a/src/main/java/noppes/npcs/controllers/data/AnimationData.java +++ b/src/main/java/noppes/npcs/controllers/data/AnimationData.java @@ -1,7 +1,7 @@ package noppes.npcs.controllers.data; -import cpw.mods.fml.common.FMLCommonHandler; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -10,6 +10,7 @@ import net.minecraft.util.AxisAlignedBB; import noppes.npcs.*; import noppes.npcs.api.entity.IAnimatable; import noppes.npcs.api.entity.ICustomNpc; +import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.handler.data.IAnimation; import noppes.npcs.api.handler.data.IAnimationData; @@ -32,7 +33,7 @@ public class AnimationData implements IAnimationData { public Animation animation; public boolean allowAnimation = false; - private Animation sentAnimation; + public Animation currentClientAnimation; private boolean isClientAnimating; private final HashSet cachedAnimationIDs = new HashSet<>(); @@ -51,6 +52,16 @@ public class AnimationData implements IAnimationData { } } + public static AnimationData getData(Entity entity) { + if (entity instanceof EntityPlayerMP) { + return PlayerData.get((EntityPlayer) entity).animationData; + } else if (entity instanceof EntityNPCInterface) { + return ((EntityNPCInterface)entity).display.animationData; + } else { + return null; + } + } + public IAnimatable getEntity() { if (this.parent instanceof DataDisplay) { EntityNPCInterface npc = ((DataDisplay)this.parent).npc; @@ -74,13 +85,13 @@ public class AnimationData implements IAnimationData { EntityLivingBase sendingEntity = parent instanceof PlayerData ? ((PlayerData) parent).player : parent instanceof DataDisplay ? ((DataDisplay) parent).npc : null; float range = parent instanceof PlayerData ? 160 : 60; if (sendingEntity != null) { - boolean prevIsClientAnimating = this.isClientAnimating && this.sentAnimation.currentFrame() != null; + boolean prevIsClientAnimating = this.isClientAnimating && this.currentClientAnimation.currentFrame() != null; this.isClientAnimating = this.allowAnimation && this.animation != null; - if (prevIsClientAnimating && (!this.isClientAnimating || this.animation != this.sentAnimation)) { - EventHooks.onAnimationEnded(this.sentAnimation); + if (prevIsClientAnimating && (!this.isClientAnimating || this.animation != this.currentClientAnimation)) { + EventHooks.onAnimationEnded(this.currentClientAnimation); } if (this.isClientAnimating) { - this.sentAnimation = this.animation; + this.currentClientAnimation = this.animation; } if (this.animation != null && this.allowAnimation) { @@ -89,10 +100,6 @@ public class AnimationData implements IAnimationData { EventHooks.onAnimationFrameEntered(this.animation, this.animation.currentFrame()); } - if (!CustomNpcs.proxy.hasClient()) { - CommonProxy.serverPlayingAnimations.add(this.animation); - } - List entities = sendingEntity.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox( sendingEntity.posX - range, sendingEntity.posY - range, sendingEntity.posZ - range, sendingEntity.posX + range, sendingEntity.posY + range, sendingEntity.posZ + range)); @@ -105,13 +112,21 @@ public class AnimationData implements IAnimationData { if (animationNBT == null && this.animation != null && !animationData.isCached(this.animation.getID())) { animationNBT = this.animation.writeToNBT(); } - animationData.viewAnimation(this.animation, sendingEntity, this, animationNBT); + animationData.viewAnimation(this.animation, this, animationNBT); } } } + public boolean isClientAnimating() { + return this.isClientAnimating; + } + public boolean isActive() { - if (!this.allowAnimation || this.animation == null || this.animation.currentFrame == this.animation.frames.size() || animation.currentFrame() == null) + return this.isActive(this.animation); + } + + public boolean isActive(Animation animation) { + if (!this.allowAnimation || animation == null || animation.currentFrame == animation.frames.size() || animation.currentFrame() == null) return false; if (this.parent instanceof DataDisplay) { @@ -152,27 +167,69 @@ public class AnimationData implements IAnimationData { this.cachedAnimationIDs.clear(); } - public void viewAnimation(Animation animation, EntityLivingBase entity, AnimationData animationData, NBTTagCompound animationNBT) { - NBTTagCompound data = animationData.writeToNBT(new NBTTagCompound()); + public void viewAnimation(Animation animation, AnimationData animationData, NBTTagCompound animationNBT) { + this.viewAnimation(animation, animationData, animationNBT, animationData.allowAnimation, -1, -1); + } + + public void viewAnimation(Animation animation, AnimationData animationData, NBTTagCompound animationNBT, boolean enabled, int currentFrame, int time) { + boolean prevEnabled = animationData.allowAnimation; + animationData.allowAnimation = enabled; + NBTTagCompound data = animationData.viewWriteNBT(new NBTTagCompound()); + animationData.allowAnimation = prevEnabled; + + synchronized (CommonProxy.serverPlayingAnimations) { + if (animationData.animationEntity != null && !animationData.animationEntity.worldObj.isRemote) { + CommonProxy.serverPlayingAnimations.add(animation); + } + } + + if (animation != null && currentFrame >= 0 && currentFrame < animation.frames.size()) { + data.setInteger("Frame", currentFrame); + data.setInteger("Time", time); + } + if (animation != null) { - if (this.cachedAnimationIDs.contains(animation.getID())) { + if (this.cachedAnimationIDs.contains(animation.getID()) && !data.hasKey("Frame")) { data.setInteger("AnimationID", animation.getID()); } else { data.setTag("Animation", animationNBT); } } + + IAnimatable animatable = animationData.getEntity(); + Entity entity = ((IEntity) animatable).getMCEntity(); if (!(entity instanceof EntityPlayer)) { data.setInteger("EntityId", entity.getEntityId()); } + Server.sendData((EntityPlayerMP) ((PlayerData) parent).player, EnumPacketClient.UPDATE_ANIMATIONS, data, entity.getCommandSenderName()); } + public NBTTagCompound viewWriteNBT(NBTTagCompound compound) { + compound.setBoolean("AllowAnimation", allowAnimation); + return compound; + } + + public void viewReadFromNBT(NBTTagCompound compound) { + this.setEnabled(compound.getBoolean("AllowAnimation")); + } + public NBTTagCompound writeToNBT(NBTTagCompound compound) { + if (this.currentClientAnimation != null) { + compound.setTag("CurrentAnimation", currentClientAnimation.writeToNBT()); + compound.setBoolean("IsClientAnimating", isClientAnimating); + } compound.setBoolean("AllowAnimation", allowAnimation); return compound; } public void readFromNBT(NBTTagCompound compound) { + if (compound.hasKey("CurrentAnimation")) { + this.currentClientAnimation = new Animation(); + this.currentClientAnimation.parent = this; + this.currentClientAnimation.readFromNBT(compound.getCompoundTag("CurrentAnimation")); + this.isClientAnimating = compound.getBoolean("IsClientAnimating"); + } this.setEnabled(compound.getBoolean("AllowAnimation")); } @@ -194,12 +251,15 @@ public class AnimationData implements IAnimationData { if (animation != null) { newAnim = new Animation(); newAnim.readFromNBT(((Animation) animation).writeToNBT()); + newAnim.currentFrame = 0; + newAnim.currentFrameTime = 0; newAnim.parent = this; } - if (CustomNpcs.proxy.hasClient() && newAnim != null) { - CommonProxy.clientPlayingAnimations.remove(this.animation); - CommonProxy.clientPlayingAnimations.add(newAnim); + synchronized (CommonProxy.clientPlayingAnimations) { + if (this.animationEntity != null && this.animationEntity.worldObj.isRemote && newAnim != null) { + CommonProxy.clientPlayingAnimations.add(newAnim); + } } this.animation = newAnim;