Jump to content

Recommended Posts

Posted

Hey fellow minecraft developers, I am working on a crop (Lemon Crop. P.S. I know it sounds wierd). I have the Lemon Seed in-game and the Lemon but, when i place the seed it crashes the game. This is extremely strange because it is giving a NullPointerExeption. I believe that it is because the Items might be registered into the game before the crops. If this is the case how can i change it. Heres is the crash log: https://pastebin.com/eZLpWgGP

 

Posted

Not strange. Almost everyone who creates a custom crop runs into this. It is because the block instance is null when you construct the seed item to your seed item tries to plan a null. Just control the order. In 1.12.2 you should be using the registry event and @ObjectHolder system for registering and instantiating. In that case, the item registration should happen after the blocks get their object holder injection.

 

Anyway, yes your suspicious is correct. It has to do with the order of registration and construction of the related classes. Just think through it and it should become obvious.

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

Posted
6 minutes ago, jabelar said:

Not strange. Almost everyone who creates a custom crop runs into this. It is because the block instance is null when you construct the seed item to your seed item tries to plan a null. Just control the order. In 1.12.2 you should be using the registry event and @ObjectHolder system for registering and instantiating. In that case, the item registration should happen after the blocks get their object holder injection.

 

Anyway, yes your suspicious is correct. It has to do with the order of registration and construction of the related classes. Just think through it and it should become obvious.

so how do i do this??

Posted (edited)

Sorry I know it can be frustrating to not get quick answers but remember we have lives. Today I had to get my daughter to the airport, have lunch with friends and now have to put in a couple hours at work. I can't look at it on my work break because my work blocks paste bin site. Ill probably be able to look at it this evening. Otherwise hopefully someone else can respond. But generally it is considered rude to bump your topic unless a full day has passed.

Edited by jabelar

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

Posted

Okay, I was able to find way to look at paste bin. You are not using the recommended way of using the registry events and @ObjectHolder annotation. The way you are doing it will not work as you cannot control their order of the static initialization between the two classes.

 

Please look how too use the new system and implement that. 

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

Posted

Okay, now I'm home and I have some time to give more specific advice. It took me a while to get into the @ObjectHolder annotation but the way you should do it is as follows:

 

In your InitBlocks and InitItems classes you should actually initialize them to null. Then you should give each class the @ObjectHolder annotation. What this does is during mod loading there are several passes after each registry event to "inject" the actual instance into the fields. The fields are matched up by the field name matching the registry name (or you can add additional annotation if you want a different field name).

 

Next you need methods that handle the registry events. I personally put those in an inner class of the class that has the @ObjectHolder instances. Those require methods regular event handling meaning the class needs the @EventBusSubscriber annotation and the methods need to be static and have the @SubscribeEvent annotation.

 

The registry events are generic so you have to handle each valid type. Such as RegistryEvent.Register<Block> and RegistryEvent.Register<Item>.  (Note you should also be using this for entities, biomes and some other things.)

 

Lastly you do the actual constructing of your singleton instances in the registry handling event.

 

You can see my example mod classes for ideas on how to put it all together: 

Now the cool thing is that if you do all this, you'll also solve your original problem because the Item registry event will happen after the Block is registered and injected into the object holder. So then when you instantiate your seed it will get the plantable block rather than null.

 

Hope that helps.

 

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

Posted
9 hours ago, jabelar said:

Okay, now I'm home and I have some time to give more specific advice. It took me a while to get into the @ObjectHolder annotation but the way you should do it is as follows:

 

In your InitBlocks and InitItems classes you should actually initialize them to null. Then you should give each class the @ObjectHolder annotation. What this does is during mod loading there are several passes after each registry event to "inject" the actual instance into the fields. The fields are matched up by the field name matching the registry name (or you can add additional annotation if you want a different field name).

 

Next you need methods that handle the registry events. I personally put those in an inner class of the class that has the @ObjectHolder instances. Those require methods regular event handling meaning the class needs the @EventBusSubscriber annotation and the methods need to be static and have the @SubscribeEvent annotation.

 

The registry events are generic so you have to handle each valid type. Such as RegistryEvent.Register<Block> and RegistryEvent.Register<Item>.  (Note you should also be using this for entities, biomes and some other things.)

 

Lastly you do the actual constructing of your singleton instances in the registry handling event.

 

You can see my example mod classes for ideas on how to put it all together: 

Now the cool thing is that if you do all this, you'll also solve your original problem because the Item registry event will happen after the Block is registered and injected into the object holder. So then when you instantiate your seed it will get the plantable block rather than null.

 

Hope that helps.

 

this would take forever tho is there not a quicker way?

 

Posted (edited)

I do admit that keeping up with latest version can be a bit of work, but it is worth doing and while a number of big things have been overhauled in recent versions I think it will settle down and everything is getting more modern/professional in their implementation.

Edited by jabelar

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

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

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • 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?
  • Topics

×
×
  • Create New...

Important Information

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