Jump to content

[1.10.2] [SOLVED] Saving additional information to chunk data


Recommended Posts

Posted (edited)

Hi,

 

I'm wondering how I can save additional information to a chunk like for example that every chunk has a different polution value

which is changed by machines. Like placing more machines in chunk x will result in a higher polution of chunk x than a chunk without

a machine has. Also machines will have different polution values depending of which machine it is, for example machine x adds 10

to the chunk polution while machine y adds 15.

 

- How can I save this additional information to every chunk?

- How can a block change this number? (make it larger when placed or the block does something, make it smaller when the player breaks the block)

 

Thx in advance.

Bektor

Edited by Bektor

Developer of Primeval Forest.

Posted

Forge has chunk-data-related events. ChunkDataEvent.Save and ChunkDataEvent.Load. Both give you the NBT of the chunk. Handle the load event, load your data, pack it into your preferred way of storing data, put it into something like a map, sync it if needed and you are done. Handle the Save event and save your data. Handle the ChunkEvent.Unload to remove data from your map. Access the map through any means you like. Not the cleanest sollution as map accessis somewhat expensive but unfortunately you can't attach a capability to a chunk as chunks are not instances of ICapabilityProvider. At least not yet.

Posted

There are three events you need:

https://github.com/Draco18s/ReasonableRealism/blob/master/src/main/java/com/draco18s/flowers/FlowerEventHandler.java#L101-L118

One for reading the data, one for writing the data, and one from freeing the data from RAM.

  • Like 1

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.

Posted

Ok.  So how do I save my data within those events now?

 

@Draco18s I see you've done it with some ChunkCoords stuff and NBTData and HashMaps etc. Also

for what are those ChunkCoords?

 

And when saving my data, the event is required to know the data to be saved, but from where should it

know the data to be saved? I mean the block which will be placed has somehow call the event and tell

it which data should be saved on top of the already existing data 

(for example existing data: 8.4f, data from new block: 1.1f -> new chunk data: 9.5f). So how am I able

to achieve this?

 

Developer of Primeval Forest.

Posted

No, you store the data in a map, modify the data in the map when needed and in your save event handling you save the data from the map that corresponds to the chunk saved.

  On 6/5/2017 at 1:06 PM, Bektor said:

Also

for what are those ChunkCoords

Expand  

They are the key for the map. Those a coordinates of the chunk you use to access and store the data in your map. 

Posted

Ok, so I've got now this code, thought I don't know if this will work the way I just implemented it.

And from where do I get the data to be saved? I mean, every block should set the data, thought I've got

no clue how I should let every block itself write the data to the chunk.

I have also no idea how the unload stuff should be implemented.

 

public class ChunkEvents {
    
    @SubscribeEvent
    public void onChunkLoad(ChunkDataEvent.Load event) {
        if(event.getWorld().isRemote)
            return;
        
        PolutionData.readData(event.getWorld(), event.getChunk().xPosition, event.getChunk().zPosition, event.getData());
    }
    
    @SubscribeEvent
    public void onChunkUnload(ChunkEvent.Unload event) {
        if(event.getWorld().isRemote)
            return;
        
        PolutionData.freeData(event.getWorld(), event.getChunk().xPosition, event.getChunk().zPosition);
    }
    
    @SubscribeEvent
    public void onChunkSave(ChunkDataEvent.Save event) {
        if(event.getWorld().isRemote)
            return;
        
        PolutionData.saveData(event.getWorld(), event.getChunk().xPosition, event.getChunk().zPosition, event.getData());
    }
}
public class PolutionData {
    
    private static final String KEY = "chunkPolution";
    // ConcurrentHashMap to allow multiply access at the same time
    private static ConcurrentHashMap<ChunkPos, Integer> data = new ConcurrentHashMap<>();
    
    public static void readData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        if(compound.hasKey(KEY)) {
            ChunkPos key = new ChunkPos(chunkX, chunkZ);
            data.put(key, compound.getInteger(KEY));
        } else {
            ChunkPos key = new ChunkPos(chunkX, chunkZ);
            data.put(key, 0);
        }
    }
    
    public static void freeData(World world, int chunkX, int chunkZ) {
        // no clue how this should work
    }
    
    public static void saveData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        NBTTagCompound nbt = new NBTTagCompound();
        data.forEach((key, value) -> {
            nbt.setInteger(key.toString(), value);
        });
        
        compound.setTag(KEY, nbt);
    }
}

 

Developer of Primeval Forest.

Posted
  On 6/5/2017 at 1:41 PM, Bektor said:

I have also no idea how the unload stuff should be implemented.

 

Expand  

Just remove your data that corresponds the chunk coordinates from a map. That's all. When a chunk is unloaded it is saved first so you are free to remove the data. 

  On 6/5/2017 at 1:41 PM, Bektor said:

I mean, every block should set the data, thought I've got

no clue how I should let every block itself write the data to the chunk.

Expand  

Upon placement of your block you get the data that corresponds to a chunk the block is placed in and do something with that data(increment the pollution for example). Upon your block being broken you do the same but you decrement the pollution. You do not need to write data to a chunk from a block, you already write the data(that is obtained from an underlying map) to a chunk every time the chunk is saved.

Posted

Also, if you want to sync data with clients on the server, you can use ChunkWatchEvent.

public void onChunkWatch(ChunkWatchEvent.Watch e)
    {
        //send sync packet to e.getPlayer();
    }

    public void onChunkUnwatch(ChunkWatchEvent.UnWatch e)
    {
        //send desync packet to e.getPlayer();
    }

 

Posted
  On 6/5/2017 at 1:50 PM, V0idWa1k3r said:

Just remove your data that corresponds the chunk coordinates from a map. That's all. When a chunk is unloaded it is saved first so you are free to remove the data. 

Upon placement of your block you get the data that corresponds to a chunk the block is placed in and do something with that data(increment the pollution for example). Upon your block being broken you do the same but you decrement the pollution. You do not need to write data to a chunk from a block, you already write the data(that is obtained from an underlying map) to a chunk every time the chunk is saved.

Expand  

Ok, but how do I get the chunkX and chunkZ positions within the onBlockAdded and breakBlock method?

So that I would be able to call something like an increment method within PolutionData which just gets the object at the given chunk position from my ConcurrentHashMap data 

and replaced the value of it with a new value.

 

  On 6/5/2017 at 7:49 PM, mrAppleXZ said:

Also, if you want to sync data with clients on the server, you can use ChunkWatchEvent.

public void onChunkWatch(ChunkWatchEvent.Watch e)
    {
        //send sync packet to e.getPlayer();
    }

    public void onChunkUnwatch(ChunkWatchEvent.UnWatch e)
    {
        //send desync packet to e.getPlayer();
    }

 

Expand  

Hm, interesting. Now I am wondering how I can send this data to the player when he looks at a specific block in the chunk or for example when he has an item in his inventory.

Developer of Primeval Forest.

Posted
  On 6/8/2017 at 8:46 PM, Bektor said:

Ok, but how do I get the chunkX and chunkZ positions within the onBlockAdded and breakBlock method?

Expand  

How would you get the chunkX and chunkZ position from any blockpos?

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.

Posted (edited)
  On 6/8/2017 at 8:52 PM, Draco18s said:

How would you get the chunkX and chunkZ position from any blockpos?

Expand  

I guess *16.

So now I'm just wondering about the data sync. How can I sync this data when the player is looking at a specific block or has a specific item in his inventory.

 

Also with my current code is the data automatically created and saved for each new and existing chunk without having to place a block from my mod?

(existing chunk when loading an old world before my mod was installed).

Edited by Bektor

Developer of Primeval Forest.

Posted
  On 6/8/2017 at 9:02 PM, Bektor said:

I guess *16.

Expand  

Why would you multiply?

  • Like 1

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.

Posted

You can also use the ChunkPos(BlockPos) constructor to convert a BlockPos to a ChunkPos.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted (edited)
  On 6/8/2017 at 11:43 PM, Draco18s said:

Why would you multiply?

Expand  

Well, I guess the idea came in mind because I worked some time with world generation where you would do chunkX * 16 stuff.

  On 6/9/2017 at 7:08 AM, Choonster said:

You can also use the ChunkPos(BlockPos) constructor to convert a BlockPos to a ChunkPos.

Expand  

Ah, totally missed this constructor.

 

Also I'm getting a null pointer somewhere in my code:

java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(Unknown Source) ~[?:1.8.0_131]
	at java.util.concurrent.FutureTask.get(Unknown Source) ~[?:1.8.0_131]
	at net.minecraft.util.Util.runTask(Util.java:29) [Util.class:?]
	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:743) [MinecraftServer.class:?]
	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:688) [MinecraftServer.class:?]
	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156) [IntegratedServer.class:?]
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:537) [MinecraftServer.class:?]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_131]
Caused by: java.lang.NullPointerException
	at minecraftplaye.justanotherenergy.common.lib.PolutionData.change(PolutionData.java:19) ~[PolutionData.class:?]
	at minecraftplaye.justanotherenergy.common.blocks.BlockSolarPanel.onBlockAdded(BlockSolarPanel.java:39) ~[BlockSolarPanel.class:?]
	at net.minecraft.world.chunk.Chunk.setBlockState(Chunk.java:660) ~[Chunk.class:?]
	at net.minecraft.world.World.setBlockState(World.java:388) ~[World.class:?]
	at net.minecraft.item.ItemBlock.placeBlockAt(ItemBlock.java:184) ~[ItemBlock.class:?]
	at net.minecraft.item.ItemBlock.onItemUse(ItemBlock.java:60) ~[ItemBlock.class:?]
	at net.minecraftforge.common.ForgeHooks.onPlaceItemIntoWorld(ForgeHooks.java:780) ~[ForgeHooks.class:?]
	at net.minecraft.item.ItemStack.onItemUse(ItemStack.java:159) ~[ItemStack.class:?]
	at net.minecraft.server.management.PlayerInteractionManager.processRightClickBlock(PlayerInteractionManager.java:509) ~[PlayerInteractionManager.class:?]
	at net.minecraft.network.NetHandlerPlayServer.processTryUseItemOnBlock(NetHandlerPlayServer.java:706) ~[NetHandlerPlayServer.class:?]
	at net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock.processPacket(CPacketPlayerTryUseItemOnBlock.java:68) ~[CPacketPlayerTryUseItemOnBlock.class:?]
	at net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock.processPacket(CPacketPlayerTryUseItemOnBlock.java:13) ~[CPacketPlayerTryUseItemOnBlock.class:?]
	at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:21) ~[PacketThreadUtil$1.class:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_131]
	at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_131]
	at net.minecraft.util.Util.runTask(Util.java:28) ~[Util.class:?]
	... 5 more
public class PolutionData { // line 11
    
    private static final String KEY = "chunkPolution";
    // ConcurrentHashMap to allow multiply access at the same time
    private static ConcurrentHashMap<ChunkPos, Integer> data = new ConcurrentHashMap<>();
    
    public static void change(BlockPos pos, int dataToChange) {
        ChunkPos chunkPos = new ChunkPos(pos);
        data.replace(chunkPos, data.get(chunkPos), (data.get(chunkPos) + dataToChange)); // line 19
    }
    
    public static int get(BlockPos pos) {
        return data.get(new ChunkPos(pos));
    }
    
    public static void readData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        if(compound.hasKey(KEY)) {
            ChunkPos key = new ChunkPos(chunkX, chunkZ);
            data.put(key, compound.getInteger(KEY));
        } else {
            ChunkPos key = new ChunkPos(chunkX, chunkZ);
            data.put(key, 0);
        }
    }
    
    public static void freeData(World world, int chunkX, int chunkZ) {
        ChunkPos pos = new ChunkPos(chunkX, chunkZ);
        if(data.containsKey(pos))
            data.remove(pos);
    }
    
    public static void saveData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        NBTTagCompound nbt = new NBTTagCompound();
        data.forEach((key, value) -> {
            nbt.setInteger(key.toString(), value);
        });
        
        compound.setTag(KEY, nbt);
    }
}
      
public class BlockSolarPanel extends Block {
	[...]
	@Override
	public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state) {
	    super.onBlockAdded(worldIn, pos, state);
	    
	    PolutionData.change(pos, 32); // line 39
	}
	
	@Override
	public void breakBlock(World worldIn, BlockPos pos, IBlockState state) {
	    super.breakBlock(worldIn, pos, state);
	    
	    PolutionData.change(pos, -32); // line 46
	}
	
	@Override
	public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand) {
	    // TODO Auto-generated method stub
	    super.updateTick(worldIn, pos, state, rand);
	    
	    System.out.println(PolutionData.get(pos)); // line 54
	}
  [...]
}

 

So as chunkPos isn't null and dataToChange is also not null and the list data can't be null as it get's initialized direclty on creation which leads to the assumption that the chunk I've placed the block in is not in the list. Thought it should be.

Edited by Bektor

Developer of Primeval Forest.

Posted
  On 6/9/2017 at 11:33 AM, Bektor said:

Well, I guess the idea came in mind because I worked some time with world generation where you would do chunkX * 16 stuff.

Expand  

That would be chunkpos -> blockpos, not blockpos -> chunkpos

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.

Posted
  On 6/9/2017 at 9:19 PM, diesieben07 said:
  1. Use << 4 and >> 4 to convert between chunk coords and block coords. Division does not work properly with negative numbers (-15 / 16 is 0, but it should be -1 for chunk coords).
  2. This is complete bullocks.

  3. What even? I assume this is trying to be threadsafe in some way? It definitely isn't, if you want to be threadsafe here use the ConcurrentMap::compute method: map.compute(key, (k, v) -> v == null ? 1 : v + 1). But you should not need to be threadsafe here. Why are you trying to be?

  4. You are getting a NPE because you are trying to add to the value in the map, but that value is null. You can't add to null.

Expand  

To 2: Well, this is what I understood what it is doing: 

  Quote

[...] supporting full concurrency of retrievals and adjustable expected concurrency for updates [...]

Expand  

To 3: Well, currently it's all running in one thread so a normal HashMap should do fine, but I'm thinking of moving some of the logic which will require this data into a different thread, as far as it is possible to do such a thing within Minecraft. Also what is the difference between ConcurrentMap::compute and ConcurrentMap::replace?

To 4: Hm, it shouldn't be null, atleast from what I wanted to do. The plan was that every chunk has as a default value a polution of 0, so there wouldn't be a null.

Developer of Primeval Forest.

Posted
  On 6/9/2017 at 10:01 PM, diesieben07 said:

Yes, but it has nothing to do with "multiple access". ConcurrentHashMap allows threads to read and modify the map concurrently.

 

First of all, read the Javadocs. But in brevity: replace checks if the value for a given key is as expected and if so, replaces it with the new value. It does all this atomically (as opposed to calling get and then put inside an if statement). This is important for thread-safety. compute applies the given function to whatever value is currently stored for the key and stores the result and again it does all this atomically. You can do everything that you can do with compute with replace, but you would need a while loop and it would be less efficient.

 

Well, you do this in readData.  readData will only be called for chunks loaded from disk, so a chunk that is freshly generated by the terrain generator will not have a default value. I suggest you just tread null in the map properly instead of trying to introduce the default value at every possible occasion a chunk might be created.

Expand  

Ah, ok. Fixed this thing now. Just having some problems with saving and reading the data to/from NBT left.

Either the data is just always 0 or with changing a few lines of NBT saving stuff, Minecraft completly crashes (removing the containsKey in saveData for example results into an NPE)

    public static void readData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        if(compound.hasKey(KEY)) {
            ChunkPos key = new ChunkPos(chunkX, chunkZ);
            data.put(key, compound.getInteger(KEY));
        }
    }
    
    public static void freeData(World world, int chunkX, int chunkZ) {
        ChunkPos pos = new ChunkPos(chunkX, chunkZ);
        if(data.containsKey(pos))
            data.remove(pos);
    }
    
    public static void saveData(World world, int chunkX, int chunkZ, NBTTagCompound compound) {
        ChunkPos key = new ChunkPos(chunkX, chunkZ);
        if(data.containsKey(key))
            compound.setInteger(KEY, data.get(key));
    }

 

Developer of Primeval Forest.

Posted (edited)
  On 6/9/2017 at 10:56 PM, diesieben07 said:

Yes, of course it will crash, because if the map does not contain the key Map::get will return null. You then try to unbox that null Integer into an int, which obviously cannot work.

What makes you think the data is 0? Have you used the debugger?

By the way, why is all this stuff static? You can't just have a global map, there can be many chunks with the same coordinates, in different dimensions. You should probably use a world capability or WorldSavedData.

Expand  

Well, this stuff is all static so because it gets called in the event methods itself, so the methods I created for ChunkDataEvent.Load, ChunkEvent.Unload and ChunkDataEvent.Save.

Also I haven't used the debugger to find out the data is 0, my block just calls the following method when I right click on it and prints the result to the console and in that case the result was always 0.

    public static int get(BlockPos pos) {
        return data.get(new ChunkPos(pos));
    }

 

As of why the map is static, its basically because that way I can move the map later into an API so other mods can easily change the polution data and can read the data to display it or do other stuff with it.

 

EDIT: Using the debug mode will also show me that the value data.get(key) within saveData is 0. And I think an easy way to solve the problem with mutliply dimensions having the same chunk coordinates would be to also store the dimension id within the map.

Edited by Bektor

Developer of Primeval Forest.

Posted
  On 6/9/2017 at 11:54 PM, diesieben07 said:

How is that even remotely close to a valid reason?

 

Expand  

Well, that way the stuff is changes always, it doesn't matter where I change it and I don't have to create a new instance of the map etc. everytime the event is called.

Also all blocks don't need to create or access some instance of it to be able to get values.

 

  On 6/9/2017 at 11:54 PM, diesieben07 said:

Well, start using the debugger. See if you even put anything in the map at all.

Expand  

Ok, I created now a new world to get rid of all old NBT data which might have been saved and the result with the debugger is that the first block placed in the chunk will always have the value 0, which should be quite easy to fix.

But it seems like the problem that it was always 0 came from a lot of testing with saving the stuff to NBT etc.

 

  On 6/9/2017 at 11:54 PM, diesieben07 said:

This is, once a gain, not a reason. You would need the mods to specify the world anyways.

 

Expand  

What do you mean by it that mods need to specify the world anyways? 

Also in your last answer you suggested using world capability or WorldSavedData. So what's exactly the difference between these two and which one would be a better choice to give also other mods access to the polution data saved per chunk so that these mods may add their own blocks and items using the values from them?

I also guess that those world capability stuff is just this thing here:  AttachCapabilityEvent<World>

 

 

  On 6/9/2017 at 11:54 PM, diesieben07 said:

This is extremely ugly and will probably leak memory all over the place unless you are very careful.

Expand  

Why would this leak memory? That way it would just be saved like it is currenlty, except for that it is not only chunkPos.toString() anymore but instead world.provider.getDimension() + chunkPos.toString()

Developer of Primeval Forest.

Posted
  On 6/10/2017 at 12:34 PM, Bektor said:

Also in your last answer you suggested using world capability or WorldSavedData. So what's exactly the difference between these two and which one would be a better choice to give also other mods access to the polution data saved per chunk so that these mods may add their own blocks and items using the values from them?

I also guess that those world capability stuff is just this thing here:  AttachCapabilityEvent<World>

Expand  

 

World Saved Data lets you store data per-dimension or per-map. World Capabilities are just a nicer wrapper around per-dimension WorldSavedData. For a public API, I recommend using Capabilities rather than World Saved Data.

 

I helped someone with a similar system and created my own implementation of it in this thread. You can see the API here and the implementation here.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
  On 6/10/2017 at 1:42 PM, Choonster said:

 

World Saved Data lets you store data per-dimension or per-map. World Capabilities are just a nicer wrapper around per-dimension WorldSavedData. For a public API, I recommend using Capabilities rather than World Saved Data.

 

I helped someone with a similar system and created my own implementation of it in this thread. You can see the API here and the implementation here.

Expand  

Ok, thx.

Just one question, why do you send a packet to the client instead of changing the value directly in your ChunkEnergy class? I mean, you basically build your system on top of the existing one from forge and the one from forge changes the energy direclty without sending an packet which does this.

Developer of Primeval Forest.

Posted (edited)
  On 6/15/2017 at 7:53 PM, Bektor said:

Just one question, why do you send a packet to the client instead of changing the value directly in your ChunkEnergy class? I mean, you basically build your system on top of the existing one from forge and the one from forge changes the energy direclty without sending an packet which does this.

Expand  

 

The server and client(s) each have their own IChunkEnergy/IChunkEnergyHolder instances. The server handles any modifications to the energy amount and syncs the new value to the relevant clients so they can render it on screen (with the Chunk Energy Display item held).

 

If there was no packet, the client-side GUI wouldn't be able to display the current energy amount.

 

Forge's energy capability doesn't have any kind of automatic client-server syncing built-in.

Edited by Choonster

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
  On 6/15/2017 at 8:01 PM, Choonster said:

 

The server and client(s) each have their own IChunkEnergy/IChunkEnergyHolder instances. The server handles any modifications to the energy amount and syncs the new value to the relevant clients so they can render it on screen (with the Chunk Energy Display item held).

 

If there was no packet, the client-side GUI wouldn't be able to display the current energy amount.

Expand  

Oh, yeah. So basically you are directly sending the new data to the client while with the forge implementation each block/item has to ask for the current value when it wants to display it, correct?

Developer of Primeval Forest.

Posted
  On 6/15/2017 at 8:04 PM, Bektor said:

Oh, yeah. So basically you are directly sending the new data to the client while with the forge implementation each block/item has to ask for the current value when it wants to display it, correct?

Expand  

 

Any mod that displays an energy value on the client needs to sync it somehow.

 

If it's an energy bar in a GUI, this is usually handled through the Container (which syncs the value when it changes). If it's rendered outside of a GUI, it could either be always synced when the value changes or synced on demand when the value needs to be rendered.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Make a test without xaerominimap
    • Hello everyone, there was a problem with my build in prism launcher, when I launch the world, the game immediately crashes https://mclo.gs/I0BNk7n can someone help please😩
    • Saving big while shopping online is easier than ever with the Temu coupon code £100 off. This deal is among the best you can get today. When you use the acs670886 Temu coupon code, you'll unlock massive benefits tailored for users in the United Kingdom and other parts of Europe. It's our way of making shopping more rewarding for everyone across the region. By using Temu coupon £100 off and Temu 100 off coupon code, you're not just saving money, you're joining a community of savvy shoppers who never pay full price. What Is The Coupon Code For Temu £100 Off? Both new and existing customers can enjoy amazing benefits using our Temu coupon £100 off. You can redeem this £100 off Temu coupon through the Temu website or mobile app easily. acs670886: Flat £100 off your purchase, no minimum order value.   acs670886: Get a £100 coupon pack for multiple uses across several categories.   acs670886: £100 flat discount specially tailored for first-time UK customers.   acs670886: Extra £100 promotional discount for existing European users.   acs670886: Exclusive £100 coupon for Temu UK users with added benefits.   Temu Coupon Code £100 Off For New Users In 2025 If you're new to Temu, you're in luck—our coupon code offers the highest perks for first-time shoppers. Use the Temu coupon £100 off and Temu coupon code £100 off to maximize your savings today. acs670886: Flat £100 discount for brand-new Temu users.   acs670886: Receive a generous £100 coupon bundle immediately after sign-up.   acs670886: Use up to £100 in coupons across multiple purchases.   acs670886: Enjoy free shipping throughout Europe.   acs670886: First-time users also get an additional 30% off on their initial order.   How To Redeem The Temu coupon £100 off For New Customers? To activate your Temu £100 coupon, create a new account on the Temu app or website. Find the coupon section in your profile and enter the Temu £100 off coupon code for new users. Apply acs670886 during checkout to see the discount reflected instantly. Temu Coupon £100 Off For Existing Customers Our discounts aren’t just for new users—existing customers can also enjoy serious savings! Use the Temu £100 coupon codes for existing users and benefit from Temu coupon £100 off for existing customers free shipping. acs670886: £100 extra discount for returning Temu shoppers.   acs670886: Coupon bundle worth £100 for multi-item purchases.   acs670886: Complimentary gift and free express delivery across Europe.   acs670886: Stackable with up to 70% discounts on select items.   acs670886: Unlimited free shipping for all UK-based customers.   How To Use The Temu Coupon Code £100 Off For Existing Customers? Log into your Temu account and go to the promo code section. Apply the Temu coupon code £100 off or Temu coupon £100 off code by entering acs670886 before checking out. Latest Temu Coupon £100 Off First Order Maximize your savings during your first order using our special code. Just apply the Temu coupon code £100 off first order, Temu coupon code first order, or Temu coupon code £100 off first time user. acs670886: Flat £100 discount for your first Temu purchase.   acs670886: Welcome bundle worth £100 just for signing up.   acs670886: Apply multiple coupon slices totaling £100.   acs670886: Free shipping extended to all EU nations.   acs670886: 30% bonus discount on top of the initial order savings.   How To Find The Temu Coupon Code £100 Off? You can discover the latest Temu coupon £100 off and Temu coupon £100 off Reddit by subscribing to Temu's newsletter. Social media pages and trusted coupon websites also share new codes weekly. Is Temu £100 Off Coupon Legit? Yes, the Temu £100 Off Coupon Legit question has a very simple answer: Absolutely! The Temu 100 off coupon legit status is confirmed through daily testing and real customer feedback. Our acs670886 coupon code is completely valid and safe for UK and EU users. Use it without worry for both new and repeat purchases. How Does Temu £100 Off Coupon Work? The Temu coupon code £100 off first-time user is a digital promo you apply during checkout. By using Temu coupon codes 100 off, you receive instant deductions or bundles applied to your total, helping you save more with every order. How To Earn Temu £100 Coupons As A New Customer? To earn your Temu coupon code £100 off, simply register on the Temu app or website as a new customer. The 100 off Temu coupon code is auto-added to your account or made available via email or push notifications. What Are The Advantages Of Using The Temu Coupon £100 Off? £100 discount on your first order   £100 coupon bundle usable across several orders   Up to 70% off on trending items   30% additional discount for UK-based loyal customers   Up to 90% off selected promotional products   Free gifts for new users   Free shipping in UK and Europe   Temu £100 Discount Code And Free Gift For New And Existing Customers By using the Temu £100 off coupon code or £100 off Temu coupon code, you enjoy unmatched shopping privileges. Whether new or returning, our users love the flexibility and value. acs670886: £100 first-order discount with instant activation   acs670886: Extra 30% on all product categories   acs670886: Receive free welcome gift as a new user   acs670886: Discount stackable with up to 70% sale prices   acs670886: Free shipping plus surprise gifts across Europe   Pros And Cons Of Using Temu Coupon Code £100 Off This Month Pros:   Flat £100 discount   Extra 30% off for UK users   Free gifts included   No expiration date   Can be combined with other offers   Cons:   One-time use per account for new users   Limited eligibility on some product categories   Terms And Conditions Of Using The Temu Coupon £100 Off In 2025 Temu coupon code £100 off free shipping available to all UK and EU users   Latest Temu coupon code £100 off is updated regularly   No expiration date for acs670886   Valid for both new and existing users   No minimum purchase requirement to activate the coupon   Final Note: Use The Latest Temu Coupon Code £100 Off You can enjoy real savings with the Temu coupon code £100 off on both your first and subsequent orders. Start shopping smarter by using this code today. The Temu coupon £100 off isn’t just a promotion—it’s your ticket to unmatched value and convenience across Europe. FAQs Of Temu £100 Off Coupon 1. What is the Temu £100 off coupon code? The code is acs670886, and it gives you up to £100 off instantly on the Temu app or website, with no minimum purchase needed. 2. Is the Temu coupon £100 off valid in 2025? Yes, this coupon is 100% valid in 2025 for both new and existing users in the UK and Europe. 3. Can existing users also benefit from the £100 coupon? Absolutely! Existing users can get an extra £100 coupon pack and additional savings like free gifts and shipping. 4. Is there a limit to how many times I can use the Temu coupon? New users can use it once, while existing users can use acs670886 across multiple purchases via the coupon bundle. 5. Where can I find more Temu coupons in 2025? Check Temu's newsletter, Reddit, and verified coupon sites to always stay updated with the newest discounts.  
    • If you're shopping for the first time on Temu, here's exciting news: you can get a Temu coupon code £100 off for your order. It’s a fantastic way to save big while exploring the thousands of items on Temu. Use our exclusive Temu coupon code acs670886 and unlock maximum benefits when shopping from the United Kingdom or any European country. Whether you're after fashion, home goods, or gadgets, this code offers an unbeatable start. With the Temu coupon £100 off and Temu 100 off coupon code, you’ll enjoy substantial discounts and access exclusive first-time offers that make your online shopping experience even more rewarding. What Is The Coupon Code For Temu £100 Off? If you’re new or even a regular shopper on Temu, there’s a huge treat for you. Use our special Temu coupon £100 off and enjoy big savings. This £100 off Temu coupon is valid for all users. acs670886: Enjoy a flat £100 discount on your order instantly.   acs670886: Get a £100 coupon pack that can be used across multiple purchases.   acs670886: Exclusive £100 flat discount for new customers.   acs670886: Receive an extra £100 off as a promo offer for existing Temu shoppers.   acs670886: Designed specifically for shoppers in the UK for maximum benefit.   Temu Coupon Code £100 Off For New Users In 2025 If you're a new user, you're in for a treat. Our Temu coupon £100 off and Temu coupon code £100 off gives you top-tier benefits right from the start. acs670886: Get a flat £100 discount as a welcome gift.   acs670886: Unlock a £100 coupon bundle curated for first-time users.   acs670886: Benefit from up to £100 in multiple-use coupon packs.   acs670886: Free shipping all over Europe with your first order.   acs670886: Enjoy an additional 30% discount on any first-time purchase.   How To Redeem The Temu coupon £100 off For New Customers? To activate your Temu £100 coupon and use the Temu £100 off coupon code for new users, follow these steps: Download the Temu app or visit the website.   Sign up for a new account using your email or social ID.   Add your desired items to the cart.   On the checkout page, locate the "Apply Coupon Code" section.   Enter acs670886 and click apply.   Enjoy your discounts and complete the purchase!   Temu Coupon £100 Off For Existing Customers Even if you're a regular Temu customer, you can still grab exclusive benefits. With our Temu £100 coupon codes for existing users and Temu coupon £100 off for existing customers free shipping, savings are guaranteed. acs670886: Receive an extra £100 off on your next Temu order.   acs670886: Access a special £100 coupon bundle for repeat customers.   acs670886: Enjoy a free gift and express delivery throughout Europe.   acs670886: Up to 70% discount stacked on your coupon benefits.   acs670886: Free delivery throughout the United Kingdom.   How To Use The Temu Coupon Code £100 Off For Existing Customers? To enjoy the Temu coupon code £100 off and make use of the Temu coupon £100 off code, follow this guide: Log into your existing Temu account.   Browse and add products to your shopping basket.   Proceed to checkout.   In the coupon field, type acs670886.   Apply the code and see the discount activate instantly.   Complete your order with massive savings.   Latest Temu Coupon £100 Off First Order The best time to save is on your first order, and we’ve made that easy. Use the Temu coupon code £100 off first order, Temu coupon code first order, and Temu coupon code £100 off first time user for incredible savings. acs670886: Enjoy a flat £100 discount on your very first purchase.   acs670886: Redeem a full £100 Temu coupon code first order benefit.   acs670886: Get up to £100 in multiple-use coupon vouchers.   acs670886: Free shipping across European countries.   acs670886: Additional 30% off on every item in your first order.   How To Find The Temu Coupon Code £100 Off? If you're looking for the latest Temu coupon £100 off and even those discussed on Temu coupon £100 off Reddit, here’s how to find them. Start by signing up for Temu’s official newsletter to receive coupons directly in your inbox. Additionally, keep an eye on Temu’s social media pages like Instagram, Facebook, and Twitter. For the latest and verified coupons, always visit trusted coupon websites that update their listings daily. Is Temu £100 Off Coupon Legit? Yes, the Temu £100 Off Coupon Legit and Temu 100 off coupon legit claims are 100% valid. Our Temu coupon code acs670886 is legitimate, safe to use, and regularly tested. You can confidently use it on both new and recurring orders in 2025. This coupon works across the UK and Europe with no expiration restrictions, ensuring you save consistently. How Does Temu £100 Off Coupon Work? The Temu coupon code £100 off first-time user and Temu coupon codes 100 off work as discount codes that you enter during checkout. Once the code is applied, you get instant deductions from your total bill, and in some cases, added perks like free shipping and free gifts. This code can also unlock bundles or extra discounts depending on your cart size and product type. It’s designed to maximise your savings and encourage smart shopping for new and regular users alike. How To Earn Temu £100 Coupons As A New Customer? You can earn the Temu coupon code £100 off and 100 off Temu coupon code simply by creating a new account and entering the code during your first purchase. The platform may also reward you for referrals, app downloads, or completing your profile. Always check your email and Temu account dashboard for exclusive coupon notifications and bonus vouchers. What Are The Advantages Of Using The Temu Coupon £100 Off? Here are the key benefits of using the Temu coupon code 100 off and Temu coupon code £100 off: £100 off on your first Temu order.   Access to a £100 coupon bundle for multiple orders.   70% off popular trending products.   Extra 30% discount for existing UK users.   Up to 90% off on selected clearance items.   Free welcome gift for first-time UK shoppers.   Complimentary delivery throughout Europe.   Temu £100 Discount Code And Free Gift For New And Existing Customers With our Temu £100 off coupon code and £100 off Temu coupon code, shopping on Temu becomes an exciting experience. Here are some of the most attractive benefits: acs670886: £100 discount applied immediately on checkout.   acs670886: Extra 30% off on your selected items.   acs670886: Free welcome gift for all new users.   acs670886: Up to 70% savings across categories.   acs670886: Free shipping and gift delivery in Europe and the UK.   Pros And Cons Of Using Temu Coupon Code £100 Off This Month Let’s explore the pros and cons of the Temu coupon £100 off code and Temu 100 off coupon: Pros: Substantial £100 discount.   Stackable with other offers.   No expiration date.   Free delivery in the UK and Europe.   Works for new and existing customers.   Cons: Limited to specific categories occasionally.   Some items may be excluded from additional discounts.   Terms And Conditions Of Using The Temu Coupon £100 Off In 2025 Below are the terms for Temu coupon code £100 off free shipping and latest Temu coupon code £100 off: No expiration date; use anytime.   Valid for both new and existing users.   Usable across the UK and Europe.   No minimum purchase required.   Must enter acs670886 at checkout for discounts to apply.   Final Note: Use The Latest Temu Coupon Code £100 Off Don’t miss your chance to save with the Temu coupon code £100 off. This is the perfect time to stock up and explore Temu’s extensive product selection with extra value. Get the best of Temu today by using your exclusive Temu coupon £100 off and enjoy shopping smarter. FAQs Of Temu £100 Off Coupon 1. What is the Temu coupon code for £100 off? You can use the code acs670886 during checkout to get a £100 discount on your order. It’s valid for both new and existing users across the UK and Europe. 2. Can I use the £100 Temu coupon more than once? Yes, you can use acs670886 for bundles and repeated benefits until the full £100 value is utilised. 3. Is the Temu £100 coupon code legit? Absolutely. The coupon code is tested, verified, and safe to use for UK and European shoppers. 4. How do I redeem the Temu coupon code as a new user? Simply create a new account on Temu, add items to your cart, and apply acs670886 during checkout. 5. Are there shipping charges with the £100 Temu coupon? No. When you apply acs670886, shipping is free throughout the UK and Europe.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.