Jump to content

Recommended Posts

Posted

Hello everyone,

I'm writing a mod to dump Minecraft's block database to a file, to get canonical data to use in WorldPainter (a map generator I've created). I'm using Block.getBlockById(int) to get each block, then getStateFromMeta(int) to get all the possible states and then getProperties() to get the properties of each state.

I'm most of the way there, but one hiccup  I'm encountering is that some of the block state properties are transient/runtime properties which are not saved to disk (if I understand correctly). Examples would be the snowy property of Dirt blocks and the occupied property of Bed blocks. These properties are not of interest to WorldPainter, so I would like to filter them out. Is there a way to tell which properties are transient/runtime and will not be saved to disk, so I can skip those?

Many thanks in advance for any assistance!

Cheers,
Captain_Chaos

Posted
14 minutes ago, Captain_Chaos said:

I'm using Block.getBlockById(int) to get each block, then getStateFromMeta(int) to get all the possible states

Bad modder, no cookie.

Integer block IDs are not guaranteed to always be the same from save to save, you must iterate over Blocks.REGISTRY.  Additionally, not all integer states are "valid" blocks.


Here's how you can get all valid block states for a given block:

BlockStateContainer bsc = block.getBlockState();
ImmutableList<IBlockState> values = bsc.getValidStates();

 

  • 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

@Draco18s thanks for your reply! I should perhaps clarify what I'm trying to do. For vanilla blocks, the block ID's and data values are guaranteed to be the same from save to save, because that's how Minecraft stores them on disk (for now). The reason I'm getting the blocks and block states this way is because I need a mapping from vanilla block ID and data value to block name and properties.

 

This is not relevant to my actual question however, which is if there is a way to know which properties are runtime or transient properties which are not stored in the chunks. BlockStateContainer.getValidStates() returns all of them, including transient properties.

Posted (edited)

@diesieben07, let me explain in more detail what I'm doing. I'm the author of WorldPainter, a map generator for Minecraft. It's a stand-alone program that generates Minecraft chunks directly to create a Minecraft map according to your own design which you can then load in Minecraft to play in. Minecraft (up to now) stores blocks and their states on disk as block IDs and data values. There are no block names or properties (at least not in a vanilla map), so I couldn't use those if I wanted to. I really do need to touch integer IDs.

Minecraft 1.13 however will store blocks by their names and properties. I would like WorldPainter to provide continuity between pre- and post 1.13 versions of Minecraft, so I want to correlate the pre-1.13 block ID and data values with their corresponding post-1.13 block names and states. For vanilla blocks only, I'm not trying to support mod blocks.

This mod I'm creating isn't intended as an actual mod to be used by anyone, I'm just using it as the quickest and easiest way to invoke the Minecraft API and interrogate its block database to establish this mapping (on the assumption that the names and states already used at runtime by MC 1.12.2 will be how they will be stored on disk by MC 1.13, which seems reasonable). I've already implemented the part everybody keeps telling me won't ever work, which is retrieving all the block names and states for every valid combo of block ID and data value and writing them to a file.

This just leaves the actual question I asked, which is if there is a way of determining which of the properties contained in the block states are ephemeral properties that are not stored on disk but calculated at runtime, such as the two examples I gave?

Edited by Captain_Chaos
linkify WorldPainter
Posted

@diesieben07, that implies that WorldPainter would just have to keep creating 1.12.2 maps forever, relying on Minecraft to convert them to 1.13 and later and never supporting any new blocks or other features past version 1.12.2. I hope you can see that that would not be very satisfactory.

Perhaps it would clarify things to take a look at WorldPainter (I've linked it in my previous message) to see what it does exactly, and why I might like to map the block IDs and data values currently used on disk by Minecraft to the names and properties it will use from version 1.13, for instance so that it can load existing worlds yet export them to MC 1.13, keep supporting exports to pre-1.13 MC, etc., etc.

Posted

 First of all, your assumption that vanilla block ids don't change is wrong. As mentioned above you need converters (and those come built-in) from older versions to newer versions. If you are worried about having your mod's users sharing older painted world files you can just run the same conversion code or copy it as a feature in your mod.

 

Within a version, you should use registry name as the unique, guaranteed-to-work key for each block, both vanilla and modded.

 

An important thing with any save / asset format is to have a versioning system. If you include the version of Minecraft used during the initial painting in your save file then it should be easy to key off that when your mod loads it.

 

Alternatively, you can save the results as a normal world save and it will all just work out on its own. Do you really need your own file format?

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

@jabelar, thanks. WorldPainter (which is not a mod) has existed since 2011 and you can still load world files from back then, and it can still export worlds to Minecraft 1.1 (not a typo), so I know a little bit about versioning, converting block IDs, etc.

I want to use registry names within a version, but I also want to correlate them to the block IDs and data values of previous versions, for various valid reasons which I've tried to explain, and are of no relevance here anyway. I'm only using Forge temporarily in order to get this information from Minecraft.

None of this is relevant to my actual question, which has nothing to do with integers, converters, etc.. I just gave this information for background, which I can now see was a mistake. I'm also not looking for programming advice. Would it be possible for someone to address the question I'm actually asking? Many thanks in advance!

  • Like 1
Posted
1 hour ago, diesieben07 said:

If you want to generate 1.13 worlds, generate 1.13 worlds.

 

5 hours ago, diesieben07 said:

Do not touch integer IDs. Ever.

 

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

Alright, I give up. What a disappointing response. This is the most cliquish, unhelpful, dismissive forum I've encountered in a long time. Not one of you took the time to understand the context, or gave me the benefit of the doubt and answered a simple question about the Forge API. All I got was high and mighty second guessing based on a cursory skim of my question as if I'm some kind of noob writing My First Mod.

You'll all be glad to know that in the mean time I figured out an alternate way to achieve my goal and I now have the complete ID to name mapping which according to you would either be impossible to get, or herald the End Times.

Thanks for your time.

  • Like 1
Posted (edited)

@Captain_Chaos We totally understand the context of your question. But you're not understanding our reservations in giving an answer.

 

Firstly, the problem is that the decision on what properties gets converted to meta (and thus saved) is hard-coded in the source for each block in the getStateFromMeta() and the getMetaFromState() methods. I suppose someone somewhere might have generated a complete table but I haven't seen it. If you really wanted this, I think you'd have to do the following:

  1. Iterate through the registry to get all registered blocks.
  2. Take a block state instance and loop through all the valid values of all the valid properties. I'm not sure the exact methods, but I've seen them before and you seem good enough with coding to track these down. 
  3. Print out the block and property values along with the getMetaFromState().
  4. Identity duplicate meta values and infer that the property that changes does not affect meta there is not saved.

I hope you understand what I mean. Basically you need to do a brute force dump because there isn't a simple mapping. That can give you the full list for the actually saved possible combinations for the current version.

 

But the next problem is that there isn't necessarily a one-to-one or even a "closed form" (like a simple table) mapping between any two Minecraft versions. As an example, fences which currently use transient states for connecting visually are apparently moving to saved block states (or maybe even separate blocks) with the 1.13 flattening. 

 

So personally, I would rely on the Minecraft world save converter code (once available) as the definitive approach to converting. Presumably they will think through all the conversions and special cases. 

Edited by jabelar
  • Like 1

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted (edited)

As you know, for any given block, only states returned by getStateFromMeta(int) are stored to disk.  Additional states returned by getValidStates() are not.  Your best bet would be to see what properties in the set of states returned by iterating through metadata 0-15 never change (and do change in getValidsStates()).  All 16 states returned for the dirt block from getStateFromMeta for instance will be snowy=false, as that is not stored.

 

This will not be true in 1.13, where blocks will not be limited to 16 (storable) states.  Presumably every possible state will be stored.

 

I'm not getting the pushback here.  His mod deals with stored world data files, and the integers are still heavily used in the anvil files.  This will change of course, but is totally applicable for a program that runs without even having Minecraft (with its nice int to blockstate mapping) running.  If you're going to deal with savefiles, there's going to be some numbers involved.  Of course things will get more complex in 1.13!  Which will of course include the numbers to blockstate mappings in the savefiles, but those are not yet in 1.12 and below, so again I really don't get the pushback against using the numbers.  You simply have to when dealing with (generating, editing) savefiles without Minecraft running.

Edited by MamiyaOtaru
  • Like 2
Posted
1 hour ago, MamiyaOtaru said:

If you're going to deal with savefiles, there's going to be some numbers involved.  Of course things will get more complex in 1.13! 

The point is, in 1.13, because of The Flattening, integer IDs may not be used at all. Dirt and Coarse Dirt will be saved as separate blocks (!)

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

I think OP should talk a bit with MCEdit devs, since this tool is known to work with vastly different save format (alpha, beta, anvil).
Anyway, the way he "give up" here and talking about "finding another solution" makes me sure i will never use WorldPainter anymore. I'm not going to risk lose my saves due to miscommunication from him.

Posted
15 hours ago, MamiyaOtaru said:

I'm not getting the pushback here. 

 

I agree. While I think his approach to invent a new format is unnecessary and therefore unadvised, there is nothing "dangerous" about what he's doing and it is his "loss" if he wants to face the extra effort and bugs.

 

In case he or other people encounter this thread in the future, the general answer to "what is complete list of all saved block properties" is what both MamiyaOtaru and I said -- you have to do your own iteration through all the getMetaFromState() methods because the properties that are saved are hard-coded. There is no simple field in the property and no master map.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Draco18s, diesieben07, maybe instead of being dicks you could write a few paragraphs on a wiki page somewhere about why people shouldn't use IDs and what they can do as an alternative.
Then when you run into this situation again you can point people toward something constructive and helpful that they can digest and understand, so that everyone has the same information before continuing the conversation.

Then Captain_Chaos or MamiyaOtaru can contribute a new section to the wiki page and everyone can learn something.

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

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

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