Posted December 26, 20168 yr Hello there. So I've been working on this 'TE base' class that allows for 'easier' creation of TEs.. However, I ran into some issue with the TE's storage tank / energy not updating until I re-logged. (Also for some reason dissipating liquid next to it seems to update it, but no other updates.). Also, another one of my TEs share each others storage, which is very odd. I've been looking for a fix for a few hours now.. and have not came upon one, so I decided to post it here: Here is the base class: public abstract class TileEntityBase extends TileEntity implements ITickable{ public final String name; public boolean isRedstonePowered; public boolean isPulseMode; public boolean stopFromDropping; protected int ticksElapsed; protected TileEntity[] tilesAround = new TileEntity[6]; protected boolean hasSavedDataOnChangeOrWorldStart; public TileEntityBase(String name){ this.name = name; } public static void init(){ ModUtil.LOGGER.info("Registering TileEntities..."); register(TileEntityMobSoulEater.class); register(TileEntityItemTeleporter.class); register(TileEntityItemLimiter.class); register(TileEntityMobLimiter.class); register(TileEntityBiomeizer.class); register(TileEntityHomeostaticGenerator.class); } private static void register(Class<? extends TileEntityBase> tileClass){ try{ String name = ModUtil.MOD_ID+":"+tileClass.newInstance().name; GameRegistry.registerTileEntity(tileClass, name); } catch(Exception e){ ModUtil.LOGGER.fatal("Registering a TileEntity failed!", e); } } @Override public final NBTTagCompound writeToNBT(NBTTagCompound compound){ this.writeSyncableNBT(compound, NBTType.SAVE_TILE); return compound; } @Override public final void readFromNBT(NBTTagCompound compound){ this.readSyncableNBT(compound, NBTType.SAVE_TILE); } @Override public final SPacketUpdateTileEntity getUpdatePacket(){ NBTTagCompound compound = new NBTTagCompound(); this.writeSyncableNBT(compound, NBTType.SYNC); return new SPacketUpdateTileEntity(this.pos, -1, compound); } @Override public final void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt){ this.readSyncableNBT(pkt.getNbtCompound(), NBTType.SYNC); } @Override public final NBTTagCompound getUpdateTag(){ NBTTagCompound compound = new NBTTagCompound(); this.writeSyncableNBT(compound, NBTType.SYNC); return compound; } @Override public final void handleUpdateTag(NBTTagCompound compound){ this.readSyncableNBT(compound, NBTType.SYNC); } public final void sendUpdate(){ if(this.world != null && !this.world.isRemote){ NBTTagCompound compound = new NBTTagCompound(); this.writeSyncableNBT(compound, NBTType.SYNC); NBTTagCompound data = new NBTTagCompound(); data.setTag("Data", compound); data.setInteger("X", this.pos.getX()); data.setInteger("Y", this.pos.getY()); data.setInteger("Z", this.pos.getZ()); PacketHandler.theNetwork.sendToAllAround(new PacketServerToClient(data, PacketHandler.TILE_ENTITY_HANDLER), new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ(), 64)); } } public void writeSyncableNBT(NBTTagCompound compound, NBTType type){ if(type != NBTType.SAVE_BLOCK){ super.writeToNBT(compound); } if(type == NBTType.SAVE_TILE){ compound.setBoolean("Redstone", this.isRedstonePowered); compound.setInteger("TicksElapsed", this.ticksElapsed); compound.setBoolean("StopDrop", this.stopFromDropping); } if(this.isRedstoneToggle() && (type != NBTType.SAVE_BLOCK || this.isPulseMode)){ compound.setBoolean("IsPulseMode", this.isPulseMode); } } public void readSyncableNBT(NBTTagCompound compound, NBTType type){ if(type != NBTType.SAVE_BLOCK){ super.readFromNBT(compound); } if(type == NBTType.SAVE_TILE){ this.isRedstonePowered = compound.getBoolean("Redstone"); this.ticksElapsed = compound.getInteger("TicksElapsed"); this.stopFromDropping = compound.getBoolean("StopDrop"); } if(this.isRedstoneToggle()){ this.isPulseMode = compound.getBoolean("IsPulseMode"); } } @Override public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newState){ return !oldState.getBlock().isAssociatedBlock(newState.getBlock()); } public String getDisplayedName(){ return StringUtil.localize("container."+ModUtil.MOD_ID+"."+this.name+".name"); } @Override public ITextComponent getDisplayName(){ return new TextComponentString(this.getDisplayedName()); } @Override public final void update(){ this.updateEntity(); } public int getComparatorStrength(){ return 0; } public void updateEntity(){ this.ticksElapsed++; if(!this.world.isRemote){ if(this instanceof ISharingEnergyProvider){ ISharingEnergyProvider provider = (ISharingEnergyProvider)this; if(provider.doesShareEnergy()){ int total = provider.getEnergyToSplitShare(); if(total > 0){ EnumFacing[] sides = provider.getEnergyShareSides(); int amount = total/sides.length; if(amount <= 0){ amount = total; } for(EnumFacing side : sides){ TileEntity tile = this.tilesAround[side.ordinal()]; if(tile != null && provider.canShareTo(tile)){ WorldUtil.doEnergyInteraction(this, tile, side, amount); } } } } } if(this instanceof ISharingFluidProvider){ ISharingFluidProvider handler = (ISharingFluidProvider)this; if(handler.doesShareFluid()){ int total = handler.getMaxFluidAmountToSplitShare(); if(total > 0){ EnumFacing[] sides = handler.getFluidShareSides(); int amount = total/sides.length; if(amount <= 0){ amount = total; } for(EnumFacing side : sides){ TileEntity tile = this.tilesAround[side.ordinal()]; if(tile != null){ WorldUtil.doFluidInteraction(this, tile, side, amount); } } } } } if(!this.hasSavedDataOnChangeOrWorldStart){ if(this.shouldSaveDataOnChangeOrWorldStart()){ this.saveDataOnChangeOrWorldStart(); } this.hasSavedDataOnChangeOrWorldStart = true; } } } public void saveDataOnChangeOrWorldStart(){ for(EnumFacing side : EnumFacing.values()){ this.tilesAround[side.ordinal()] = this.world.getTileEntity(this.pos.offset(side)); } } public boolean shouldSaveDataOnChangeOrWorldStart(){ return this instanceof ISharingEnergyProvider || this instanceof ISharingFluidProvider; } public void setRedstonePowered(boolean powered){ this.isRedstonePowered = powered; this.markDirty(); } public boolean canPlayerUse(EntityPlayer player){ return player.getDistanceSq(this.getPos().getX()+0.5D, this.pos.getY()+0.5D, this.pos.getZ()+0.5D) <= 64 && !this.isInvalid() && this.world.getTileEntity(this.pos) == this; } protected boolean sendUpdateWithInterval(){ if(this.ticksElapsed%ConfigIntValues.TILE_ENTITY_UPDATE_INTERVAL.getValue() == 0){ this.sendUpdate(); return true; } else{ return false; } } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing){ return this.getCapability(capability, facing) != null; } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing){ if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY){ IItemHandler handler = this.getItemHandler(facing); if(handler != null){ return (T)handler; } } else if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY){ IFluidHandler tank = this.getFluidHandler(facing); if(tank != null){ return (T)tank; } } else if(capability == CapabilityEnergy.ENERGY){ IEnergyStorage storage = this.getEnergyStorage(facing); if(storage != null){ return (T)storage; } } return super.getCapability(capability, facing); } public IFluidHandler getFluidHandler(EnumFacing facing){ return null; } public IEnergyStorage getEnergyStorage(EnumFacing facing){ return null; } public IItemHandler getItemHandler(EnumFacing facing){ return null; } public boolean isRedstoneToggle(){ return false; } public void activateOnPulse(){ } public boolean respondsToPulses(){ return this.isRedstoneToggle() && this.isPulseMode; } public enum NBTType{ SAVE_TILE, SYNC, SAVE_BLOCK } } and here is the TE that doesnt update properly: public class TileEntityHomeostaticGenerator extends TileEntityBase implements ISharingEnergyProvider, ISharingFluidProvider { public final CustomEnergyStorage storage = new CustomEnergyStorage(150000, 0, 120); public final FluidTank tankLava = new FluidTank(8*Util.BUCKET); public final FluidTank tankWater = new FluidTank(8*Util.BUCKET); private final FluidHandlerFluidMap handlerMap; public final int DRAIN_PER_TICK = 5; private static final int BASE_PRODUCE = 30; private int lastEnergy; private int lastLavaTank; private int lastWaterTank; public TileEntityHomeostaticGenerator() { super("homeostaticGenerator"); this.handlerMap = new FluidHandlerFluidMap(); this.handlerMap.addHandler(FluidRegistry.WATER, this.tankWater); this.handlerMap.addHandler(FluidRegistry.LAVA, this.tankLava); } @Override public void writeSyncableNBT(NBTTagCompound compound, NBTType type){ this.storage.writeToNBT(compound); this.tankWater.writeToNBT(compound); this.tankLava.writeToNBT(compound); super.writeSyncableNBT(compound, type); } @Override public void readSyncableNBT(NBTTagCompound compound, NBTType type){ this.storage.readFromNBT(compound); this.tankWater.readFromNBT(compound); this.tankLava.readFromNBT(compound); super.readSyncableNBT(compound, type); } @Override public void updateEntity() { super.updateEntity(); if(!world.isRemote) { boolean canProcess; if(tankWater.getFluidAmount() != 0 && tankLava.getFluidAmount() != 0) { canProcess=true; }else { canProcess=false; } if(canProcess) { this.tankLava.drainInternal(new FluidStack(FluidRegistry.LAVA, DRAIN_PER_TICK), true); this.tankWater.drainInternal(new FluidStack(FluidRegistry.WATER, DRAIN_PER_TICK), true); this.storage.receiveEnergyInternal(BASE_PRODUCE, false); } if(storage.getEnergyStored() != this.lastEnergy || tankWater.getFluidAmount() != this.lastWaterTank || tankLava.getFluidAmount() != this.lastLavaTank && this.sendUpdateWithInterval()) { this.lastEnergy = storage.getEnergyStored(); this.lastLavaTank = tankLava.getFluidAmount(); this.lastWaterTank = tankWater.getFluidAmount(); } } } @Override public int getEnergyToSplitShare(){ return this.storage.getEnergyStored(); } @Override public boolean doesShareEnergy(){ return true; } @Override public EnumFacing[] getEnergyShareSides(){ return EnumFacing.values(); } @Override public boolean canShareTo(TileEntity tile){ return true; } @Override public IEnergyStorage getEnergyStorage(EnumFacing facing){ return this.storage; } @Override public IFluidHandler getFluidHandler(EnumFacing facing){ return handlerMap; } @Override public int getMaxFluidAmountToSplitShare(){ return 0; } @Override public boolean doesShareFluid(){ return false; } @Override public EnumFacing[] getFluidShareSides(){ return null; } } Here is the TE that 'saves' its energy among all of itself: public class TileEntityBiomeizer extends TileEntityBase implements IEnergyDisplay, ISharingEnergyProvider { public static final int UKNOWN_BIOME_AVERAGE = 10; public static CustomEnergyStorage storage = new CustomEnergyStorage(250000, 0, 5000); public TileEntityBiomeizer() { super("biomeizer"); } @Override public void updateEntity() { super.updateEntity(); if(!world.isRemote) { this.storage.receiveEnergyInternal(this.getEnergyFromBiome(this.getLocalBiome()), false); } } public int getEnergyFromBiome(Biome biome) { if(BiomeUtility.isVaildBiome(biome)) { float temp = biome.getTemperature(); boolean canRain = biome.canRain(); float heightVariation = biome.getHeightVariation(); boolean isHumid = biome.isHighHumidity(); return energyAmount(temp,canRain,heightVariation,isHumid); } return 0; } public int energyAmount(float temp, boolean canRain, float heightVar, boolean isHighHumid) { int energyToReturn = 0; if(1.95-temp >= 0) { energyToReturn+=2; }else { energyToReturn+=1; } if(canRain) { energyToReturn+=2; } if(.4-heightVar >= 0) { energyToReturn+=5; }else { energyToReturn+=2; } if(isHighHumid) { energyToReturn+=5; } return energyToReturn; } public Biome getLocalBiome() { return this.world.getBiome(this.getPos()); } @Override public int getEnergyToSplitShare(){ return this.storage.getEnergyStored(); } @Override public boolean doesShareEnergy(){ return true; } @Override public EnumFacing[] getEnergyShareSides(){ return EnumFacing.values(); } @Override public boolean canShareTo(TileEntity tile){ return true; } @Override public CustomEnergyStorage getEnergyStorage(){ return this.storage; } @Override public boolean needsHoldShift() { return false; } } And the 'CustomEnergyStorage' class: import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.energy.EnergyStorage; public class CustomEnergyStorage extends EnergyStorage{ public CustomEnergyStorage(int capacity, int maxReceive, int maxExtract){ super(capacity, maxReceive, maxExtract); } public int extractEnergyInternal(int maxExtract, boolean simulate){ int before = this.maxExtract; this.maxExtract = Integer.MAX_VALUE; int toReturn = this.extractEnergy(maxExtract, simulate); this.maxExtract = before; return toReturn; } public int receiveEnergyInternal(int maxReceive, boolean simulate){ int before = this.maxReceive; this.maxReceive = Integer.MAX_VALUE; int toReturn = this.receiveEnergy(maxReceive, simulate); this.maxReceive = before; return toReturn; } @Override public int receiveEnergy(int maxReceive, boolean simulate){ if(!this.canReceive()){ return 0; } int energy = this.getEnergyStored(); int energyReceived = Math.min(this.capacity-energy, Math.min(this.maxReceive, maxReceive)); if(!simulate){ this.setEnergyStored(energy+energyReceived); } return energyReceived; } @Override public int extractEnergy(int maxExtract, boolean simulate){ if(!this.canExtract()){ return 0; } int energy = this.getEnergyStored(); int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract)); if(!simulate){ this.setEnergyStored(energy-energyExtracted); } return energyExtracted; } public void readFromNBT(NBTTagCompound compound){ this.setEnergyStored(compound.getInteger("Energy")); } public void writeToNBT(NBTTagCompound compound){ compound.setInteger("Energy", this.getEnergyStored()); } public void setEnergyStored(int energy){ this.energy = energy; } } Also, I do indeed send my packets correctly. Thanks for your time. Relatively new to modding. Currently developing: https://github.com/LambdaXV/DynamicGenerators
December 26, 20168 yr if(type != NBTType.SAVE_BLOCK){ super.writeToNBT(compound); } You do know that a TE has to call super.writeToNBT() to be properly saved to disk, right? Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
December 26, 20168 yr Author Thats besides the point, fixed it, but it still doesnt sync with client, here are some packet stuff: Handler public final class PacketHandler{ public static final List<IDataHandler> DATA_HANDLERS = new ArrayList<IDataHandler>(); public static final IDataHandler PARTICLE_HANDLER = new IDataHandler(){ @Override @SideOnly(Side.CLIENT) public void handleData(NBTTagCompound compound){ AssetUtil.spawnLaserWithTimeClient(compound.getDouble("StartX"), compound.getDouble("StartY"), compound.getDouble("StartZ"), compound.getDouble("EndX"), compound.getDouble("EndY"), compound.getDouble("EndZ"), new float[]{compound.getFloat("Color1"), compound.getFloat("Color2"), compound.getFloat("Color3")}, compound.getInteger("MaxAge"), compound.getDouble("RotationTime"), compound.getFloat("Size"), compound.getFloat("Alpha")); } }; public static final IDataHandler TILE_ENTITY_HANDLER = new IDataHandler(){ @Override @SideOnly(Side.CLIENT) public void handleData(NBTTagCompound compound){ World world = Minecraft.getMinecraft().world; if(world != null){ TileEntity tile = world.getTileEntity(new BlockPos(compound.getInteger("X"), compound.getInteger("Y"), compound.getInteger("Z"))); if(tile instanceof TileEntityBase){ ((TileEntityBase)tile).readSyncableNBT(compound.getCompoundTag("Data"), TileEntityBase.NBTType.SYNC); } } } }; public static final IDataHandler GUI_BUTTON_TO_TILE_HANDLER = new IDataHandler(){ @Override public void handleData(NBTTagCompound compound){ World world = DimensionManager.getWorld(compound.getInteger("WorldID")); TileEntity tile = world.getTileEntity(new BlockPos(compound.getInteger("X"), compound.getInteger("Y"), compound.getInteger("Z"))); if(tile instanceof IButtonReactor){ IButtonReactor reactor = (IButtonReactor)tile; Entity entity = world.getEntityByID(compound.getInteger("PlayerID")); if(entity instanceof EntityPlayer){ reactor.onButtonPressed(compound.getInteger("ButtonID"), (EntityPlayer)entity); } } } }; public static final IDataHandler GUI_BUTTON_TO_CONTAINER_HANDLER = new IDataHandler(){ @Override public void handleData(NBTTagCompound compound){ World world = DimensionManager.getWorld(compound.getInteger("WorldID")); Entity entity = world.getEntityByID(compound.getInteger("PlayerID")); if(entity instanceof EntityPlayer){ Container container = ((EntityPlayer)entity).openContainer; if(container instanceof IButtonReactor){ ((IButtonReactor)container).onButtonPressed(compound.getInteger("ButtonID"), (EntityPlayer)entity); } } } }; public static final IDataHandler GUI_NUMBER_TO_TILE_HANDLER = new IDataHandler(){ @Override public void handleData(NBTTagCompound compound){ World world = DimensionManager.getWorld(compound.getInteger("WorldID")); TileEntity tile = world.getTileEntity(new BlockPos(compound.getInteger("X"), compound.getInteger("Y"), compound.getInteger("Z"))); if(tile instanceof INumberReactor){ INumberReactor reactor = (INumberReactor)tile; reactor.onNumberReceived(compound.getInteger("Number"), compound.getInteger("NumberID"), (EntityPlayer)world.getEntityByID(compound.getInteger("PlayerID"))); } } }; public static final IDataHandler GUI_STRING_TO_TILE_HANDLER = new IDataHandler(){ @Override public void handleData(NBTTagCompound compound){ World world = DimensionManager.getWorld(compound.getInteger("WorldID")); TileEntity tile = world.getTileEntity(new BlockPos(compound.getInteger("X"), compound.getInteger("Y"), compound.getInteger("Z"))); if(tile instanceof IStringReactor){ IStringReactor reactor = (IStringReactor)tile; reactor.onTextReceived(compound.getString("Text"), compound.getInteger("TextID"), (EntityPlayer)world.getEntityByID(compound.getInteger("PlayerID"))); } } }; public static final IDataHandler CHANGE_PLAYER_DATA_HANDLER = new IDataHandler(){ @Override public void handleData(NBTTagCompound compound){ NBTTagCompound data = compound.getCompoundTag("Data"); UUID id = compound.getUniqueId("UUID"); PlayerData.getDataFromPlayer(id).readFromNBT(data, false); if(compound.getBoolean("Log")){ ModUtil.LOGGER.info("Receiving (new or changed) Player Data for player with UUID "+id+"."); } } }; public static SimpleNetworkWrapper theNetwork; public static void init(){ theNetwork = NetworkRegistry.INSTANCE.newSimpleChannel(ModUtil.MOD_ID); theNetwork.registerMessage(PacketServerToClient.Handler.class, PacketServerToClient.class, 0, Side.CLIENT); theNetwork.registerMessage(PacketClientToServer.Handler.class, PacketClientToServer.class, 1, Side.SERVER); DATA_HANDLERS.add(PARTICLE_HANDLER); DATA_HANDLERS.add(TILE_ENTITY_HANDLER); DATA_HANDLERS.add(GUI_BUTTON_TO_TILE_HANDLER); DATA_HANDLERS.add(GUI_STRING_TO_TILE_HANDLER); DATA_HANDLERS.add(GUI_NUMBER_TO_TILE_HANDLER); DATA_HANDLERS.add(CHANGE_PLAYER_DATA_HANDLER); DATA_HANDLERS.add(GUI_BUTTON_TO_CONTAINER_HANDLER); } } which is registered in my preinit: PacketHandler.init(); Thanks. Relatively new to modding. Currently developing: https://github.com/LambdaXV/DynamicGenerators
December 26, 20168 yr You don't need to send packets to update TEs. Vanilla does that automatically. It's what onDataPacket and handleUpdateTag are for. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
December 27, 20168 yr Author Ah okay.. Then why doesnt my TE update? Relatively new to modding. Currently developing: https://github.com/LambdaXV/DynamicGenerators
December 27, 20168 yr I don't know. Your writeToNbtSync method is weird and I don't know why you have it at all. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
December 27, 20168 yr Author I dont think that is what the problem is.. Its deffintally something with the server not sending the client updates.. The display (which includes the energy and storage) is what is not updating, only certain block updates / re-logging will update it.. Any way, So your saying I should just get rid of the NBT_TYPES all together? I use it to save certain data when needed. Relatively new to modding. Currently developing: https://github.com/LambdaXV/DynamicGenerators
December 27, 20168 yr The reason it isn't syncing when the data changes likely has to do with your writeToNbtSync methods not sending the data due to an incorrect assumption on which data is important. getUpdatePacket and getUpdateTag are called at different times, I forget which is which, but one is called when the TE is first sent to the client (either when the client logs in, or when the chunk is first loaded and sent to the client). The other is called after that, when the TE data changes and the client needs to know about it. Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.