Jump to content

Recommended Posts

Posted

I've been working through and learning how to use events and I've run into a problem.

 

This is the code in question:

 

 

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

 

if(Keyboard.isKeyDown(43)) {

event.entity.motionY = 4.0D;

}

}

 

 

 

This works fine in single player but throws and error in multiplayer:

 

 

[23:06:17] [server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.living.LivingEvent$LivingUpdateEvent@2216b29c:

java.lang.IllegalStateException: Keyboard must be created before you can query key state

at org.lwjgl.input.Keyboard.isKeyDown(Keyboard.java:406) ~[lwjgl-2.9.1.jar:?]

at robo51.newt.handlers.EventsHandler.onLivingUpdateEvent(EventsHandler.java:48) ~[EventsHandler.class:?]

at cpw.mods.fml.common.eventhandler.ASMEventHandler_6_EventsHandler_onLivingUpdateEvent_LivingUpdateEvent.invoke(.dynamic) ~[?:?]

at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]

at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:138) [EventBus.class:?]

at net.minecraftforge.common.ForgeHooks.onLivingUpdate(ForgeHooks.java:282) [ForgeHooks.class:?]

at net.minecraft.entity.EntityLivingBase.onUpdate(EntityLivingBase.java:1763) [EntityLivingBase.class:?]

at net.minecraft.entity.player.EntityPlayer.onUpdate(EntityPlayer.java:327) [EntityPlayer.class:?]

at net.minecraft.entity.player.EntityPlayerMP.onUpdateEntity(EntityPlayerMP.java:330) [EntityPlayerMP.class:?]

at net.minecraft.network.NetHandlerPlayServer.processPlayer(NetHandlerPlayServer.java:329) [NetHandlerPlayServer.class:?]

at net.minecraft.network.play.client.C03PacketPlayer.processPacket(C03PacketPlayer.java:37) [C03PacketPlayer.class:?]

at net.minecraft.network.play.client.C03PacketPlayer$C06PacketPlayerPosLook.processPacket(C03PacketPlayer.java:271) [C03PacketPlayer$C06PacketPlayerPosLook.class:?]

at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:241) [NetworkManager.class:?]

at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) [NetworkSystem.class:?]

at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) [MinecraftServer.class:?]

at net.minecraft.server.dedicated.DedicatedServer.updateTimeLightAndEntities(DedicatedServer.java:349) [DedicatedServer.class:?]

at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) [MinecraftServer.class:?]

at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]

at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]

 

 

 

Now after doing a little looking around I've figured out that I could  register a key binder to help fix this.

But I'm wondering it there is an easier way since I'm just trying to check for the space-bar (or jump key) press.

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

There's some way to check for the vanilla keys. I forget how. Key bindings don't exist on the server though. You'll probably need to send a packet.

Check out my mod, Realms of Chaos, here.

 

If I helped you, be sure to press the "Thank You" button!

Posted

I see, I have no idea what that would entail. :/

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

So after a few busy days I had a chance to sit down and try and work this out, and I feel like I have hit a wall with my face. :/

 

I ended up using Ablaze's How to use SimpleNetworkWrapper, IMessage and IMessageHandler tutorial.

 

I have these two classes from that:

 

NewtMessageHandler

 

 

package robo51.newt.handlers;

 

import cpw.mods.fml.common.network.simpleimpl.IMessage;

import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;

import cpw.mods.fml.common.network.simpleimpl.MessageContext;

 

public class NewtMessageHandler implements IMessageHandler<NewtMessage, IMessage> {

 

@Override

public IMessage onMessage(NewtMessage message, MessageContext ctx) {

// TODO Auto-generated method stub

return null;

}

 

}

 

 

 

 

NewtMessage

 

 

package robo51.newt.handlers;

 

import io.netty.buffer.ByteBuf;

import cpw.mods.fml.common.network.simpleimpl.IMessage;

 

public class NewtMessage implements IMessage {

 

@Override

public void fromBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

@Override

public void toBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

}

 

 

 

 

But I'm confused as to what I am supposed to put in these.

I think I need to send a packet from my event handler using these to the server?

 

But what do I need to send? Do I send the Keyboard.isKeyDown?

And then does my event handler get somethings back to execute the code or is that supposed to happen somewhere else?

 

I feel like these are easy questions to answer, but I'm not capable of answering them. :/

 

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

Your IMessage class needs to have whatever data you need to pass, and a constructor that allows you to pass them.

 

Keys are tricky, because all of the keyboard information is client-side only, so sending the keyCode of the key pressed is pointless. Instead, I recommend you make separate packets for each action that you want to perform, or at least create some way to distinguish actions.

 

A simple example:

public class NewtMessage implements IMessage {
private byte action;

public NewtMessage(KeyBinding kb) {
if (kb == YourKeys.yourKeyBindings.specialKey) {
action = (byte) 1;
} else { // whatever other actions
action = (byte) 2;
}
}

// write the 'action' byte to the buffer
// read it back

// in your IMessageHandler class' method:
switch(message.action) {
case 1: // do your special action # 1
case 2: // do special action # 2
default: // throw an exception
}
}

 

Obviously that is not optimal code, but it should illustrate what you need. If you want to see some concrete examples, feel free to browse my code.

 

EDIT: Keep in mind that my current code base is for Forge 1180; if you are using 1217+, you will need to also check Keyboard.getEventKeyState() to tell you if the key was pressed (true) or released (false), since KeyInputEvent is now fired for both presses and releases (yay!!!).

Posted

So I been working on this again a bit and worked through a few things.

 

Now I'm having trouble figuring out how to properly implement the packet sending into what I have already.

 

This is my eventshandler:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.entity.EntityLivingBase;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.DamageSource;

import net.minecraft.world.World;

import net.minecraftforge.event.entity.EntityEvent.EntityConstructing;

import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.entity.ExtendedPlayerWater;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.relauncher.Side;

import cpw.mods.fml.relauncher.SideOnly;

 

 

public class EventsHandler {

 

@SubscribeEvent

public void onLivingUpdateEvent(LivingUpdateEvent event, World world) {

EntityLivingBase entity;

 

if (event.entity instanceof EntityPlayer) {

EntityPlayer player = (EntityPlayer) event.entity;

if(event.entity.isWet()) {

int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();

if(watersealing <= 0) {

event.entity.attackEntityFrom(DamageSource.drown, 1.0F);

} else {

--watersealing;

ExtendedPlayerWater.newCurrentWaterSealing(watersealing);

if(watersealing == 200) {

if(!world.isRemote)

player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));

}

if(watersealing == 50) {

if(!world.isRemote)

player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));

}

}

}

/*

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

 

if(Keyboard.isKeyDown(43)) {

event.entity.motionY = 4.0D;

}

} */

}

}

 

@SubscribeEvent

public void onEntityConstructing(EntityConstructing event) {

if (event.entity instanceof EntityPlayer && ExtendedPlayerWater.get((EntityPlayer) event.entity) == null) {

ExtendedPlayerWater.register((EntityPlayer) event.entity);

}

}

}

 

 

 

This is the message that i'm trying to use:

 

 

package robo51.newt.network.packet.server;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.Newt;

import net.minecraft.entity.player.EntityPlayer;

import io.netty.buffer.ByteBuf;

import cpw.mods.fml.common.network.simpleimpl.IMessage;

import cpw.mods.fml.common.network.simpleimpl.MessageContext;

 

/**

*

* A simple message telling the server that the client wants to open a GUI.

*

*/

public class RiseUpMessage implements IMessage {

 

// The basic, no-argument constructor MUST be included to use the new automated handling

public RiseUpMessage() {}

 

public static class Handler extends AbstractServerMessageHandler<RiseUpMessage> {

@Override

public IMessage handleServerMessage(EntityPlayer player, RiseUpMessage message, MessageContext ctx) {

if(player.isInWater()) {

player.setAir(10);

player.motionY = 0.005D;

if(player.isCollidedHorizontally) {

player.motionY = 0.2D;

} else {

player.motionY = -1.0D;

}

}

return null;

}

}

 

@Override

public void fromBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

 

@Override

public void toBytes(ByteBuf buf) {

// TODO Auto-generated method stub

 

}

}

 

 

 

I also added a KeyHandler for later, not sure if I need something else in this:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.client.settings.KeyBinding;

import net.minecraft.util.StatCollector;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.network.PacketDispatcher;

import robo51.newt.network.packet.server.RiseUpMessage;

import cpw.mods.fml.client.registry.ClientRegistry;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.common.gameevent.InputEvent.KeyInputEvent;

 

 

public class KeyHandler {

/** Storing an instance of Minecraft in a local variable saves having to get it every time */

private final Minecraft mc;

 

/** Key index for easy handling */

public static final int CUSTOM_INV = 0;

 

/** Key descriptions; use a language file to localize the description later */

private static final String[] desc = {"key.tut_inventory.desc"};

 

/** Default key values */

private static final int[] keyValues = {Keyboard.KEY_SPACE};

 

/** Make this public or provide a getter if you'll need access to the key bindings from elsewhere */

public static final KeyBinding[] keys = new KeyBinding[desc.length];

 

public KeyHandler() {

mc = Minecraft.getMinecraft();

for (int i = 0; i < desc.length; ++i) {

keys = new KeyBinding(desc, keyValues, StatCollector.translateToLocal("key.tutorial.label"));

ClientRegistry.registerKeyBinding(keys);

}

}

 

/**

* KeyInputEvent is in the FML package, so we must register to the FML event bus

*/

@SubscribeEvent

public void onKeyInput(KeyInputEvent event) {

// checking inGameHasFocus prevents your keys from firing when the player is typing a chat message

// NOTE that the KeyInputEvent will NOT be posted when a gui screen such as the inventory is open

// so we cannot close an inventory screen from here; that should be done in the GUI itself

if (mc.inGameHasFocus) {

if (keys[CUSTOM_INV].getIsKeyPressed()) {

PacketDispatcher.sendToServer(new RiseUpMessage());

}

}

}

}

 

 

 

I've also been having trouble with the chat components in the EventsHandler, I believe it has to to with the

world parameter that I added to prevent double chat messages.

 

 

How would I properly implement the packet?

I'm just not sure what the server and the client need to handle.

 

And some help with the world/chat problem would be much appreciated. :)

 

Also, Happy Halloween!

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

You CAN NOT add parameters to event handling methods - they take one argument and one argument only: the event class for which they will fire.

 

@SubscribeEvent

public void whateverMethodName(Event event) {}

 

That is the signature your method MUST have. Always use the most specific Event subclass that you can, e.g. LivingUpdateEvent instead of LivingEvent.

 

If you need something that is not part of the event (such as a 'World'), then you must find another way to get it or use a different event. For world objects, it is very easy - every entity has a world object: event.entity.worldObj.

Posted

You CAN NOT add parameters to event handling methods - they take one argument and one argument only: the event class for which they will fire.

 

@SubscribeEvent

public void whateverMethodName(Event event) {}

 

That is the signature your method MUST have. Always use the most specific Event subclass that you can, e.g. LivingUpdateEvent instead of LivingEvent.

 

If you need something that is not part of the event (such as a 'World'), then you must find another way to get it or use a different event. For world objects, it is very easy - every entity has a world object: event.entity.worldObj.

 

Thank you very much for the world help, It's much appreciated :)

 

Would you perhaps be able to help me with the packet?

 

I'm having trouble understanding where I would need to break off in this code:

 

 

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

}

 

 

 

I know the server doesn't handle key-binds, does that mean the packet should contain the isKeyDown or would it be everything after?

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

Your approach to this is kinda bad and not very well planned.

Inside packets (in this case) you shouldn't check anything, no IFs, nothing like that. Packets are used for one purpose - synchronize client with server. By that I mean - you just send one tiny bit of info that will be later used by any side said above and maybe "answered".

 

Let's rethink this:

1. Everything should happen server-side.

2. You have your ExtendedPlayerWater, from what I read in code - this is your ExtendedPlayer class that is holding some value ("watersealing").

3. You want it, so the player has 200 ticks of "extra" "something", BUT ONLY IF he's holding a key down, ay? (detail - 20 ticks = 1 sec, you wrote "200" and then "5 Seconds left").

 

How would I approach this:

1. In your ExtendedPlayerWater create one BOOLEAN value "isUsing".

2. Look at KeyBinding.class - you can call for action on both press and "unpress".

3. Create KeyBinding that will call for packet "ChangeUseState" on either press or unpress (doesn't matter, if it's 0 then it will change to 1, otherwise from 1 to 0).

4. Now create a "ChangeUseState" packet that will send send notification from client to server when the binding from 3. is pressed. In this case you don't even need input, only thing required is ofc. sender (player).

5. On server-receiver you get sender of this packed get his "isUsing" and if(isUsing==0) isUsing = 1 else(otherwise).

6. WHOLE rest of this mess will be handled by server itself, so:

In your "LivingUpdateEvent event":

- I would use PlayerTickEvent instead.

- You make all the checks like you did before but instead of checking the binding, just look into your ExtendedPlayerWater.isUsing.

 

Regarding pressing and "unpressing":

You can send packed on press, on stop pressing, on re-press (double-press), anything you want rly.

 

Regarding packets:

His tutorial is probably best you can find. Until you understand how it works I am not planning on providing done code. Give me something to work with.

1.7.10 is no longer supported by forge, you are on your own.

Posted

You can get away with running that code you posted on the client side only.

 

Is this necessarily true? Bear with me, I'm a noob, but I thought the server checks for "proper" movement of the player as an anti-cheat measure. If a mod is moving the player entity in ways that the server doesn't know about, couldn't that potentially cause the player to be kicked if the mod moves the player entity too fast or in a way that the server considers "flight"?

Posted

Is this necessarily true? Bear with me, I'm a noob, but I thought the server checks for "proper" movement of the player as an anti-cheat measure. If a mod is moving the player entity in ways that the server doesn't know about, couldn't that potentially cause the player to be kicked if the mod moves the player entity too fast or in a way that the server considers "flight"?

That is possible, but not likely if you use fairly normal motion values. The code for 'moving wrongly' on the server simply checks if the difference between the player's current and last position is greater than a certain value, so as long as you are within that, you are fine. If you need more than that, then yes, you will probably need to send a packet and handle the position change on the server, but I've never had to do that.

Posted

Maybe I'll explain what I'm tying to accomplish better.

 

I'm changing how the player reacts with water, in my EventsHandler:

 

 

package robo51.newt.handlers;

 

import net.minecraft.client.Minecraft;

import net.minecraft.entity.EntityLivingBase;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.DamageSource;

import net.minecraft.world.World;

import net.minecraftforge.event.entity.EntityEvent.EntityConstructing;

import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;

 

import org.lwjgl.input.Keyboard;

 

import robo51.newt.entity.ExtendedPlayerWater;

import cpw.mods.fml.common.eventhandler.SubscribeEvent;

import cpw.mods.fml.common.gameevent.TickEvent.PlayerTickEvent;

import cpw.mods.fml.relauncher.Side;

import cpw.mods.fml.relauncher.SideOnly;

 

 

public class EventsHandler {

 

@SubscribeEvent

public void playerTickEvent(PlayerTickEvent event) {

EntityLivingBase entity;

 

if (event.player instanceof EntityPlayer) {

EntityPlayer player = (EntityPlayer) event.player;

if(event.player.isWet()) {

int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();

if(watersealing <= 0) {

event.player.attackEntityFrom(DamageSource.drown, 1.0F);

} else {

--watersealing;

ExtendedPlayerWater.newCurrentWaterSealing(watersealing);

if(watersealing == 200) {

if(!event.player.worldObj.isRemote) {

player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));

}

}

if(watersealing == 50) {

if(!event.player.worldObj.isRemote) {

player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));

}

}

}

}

/*

if(event.entity.isInWater()) {

event.entity.setAir(10);

if(Keyboard.isKeyDown(57)) {

event.entity.motionY = 0.005D;

if(event.entity.isCollidedHorizontally) {

event.entity.motionY = 0.2D;

}

} else {

event.entity.motionY = -1.0D;

}

} */

}

}

 

@SubscribeEvent

public void onEntityConstructing(EntityConstructing event) {

if (event.entity instanceof EntityPlayer && ExtendedPlayerWater.get((EntityPlayer) event.entity) == null) {

ExtendedPlayerWater.register((EntityPlayer) event.entity);

}

}

}

 

 

This block of code:

		if(event.player.isWet()) {
			int watersealing = ExtendedPlayerWater.getCurrentWaterSealing();
			if(watersealing <= 0) {
				event.player.attackEntityFrom(DamageSource.drown, 1.0F);
			} else {
				--watersealing;
				ExtendedPlayerWater.newCurrentWaterSealing(watersealing);
				if(watersealing == 200) {
					if(!event.player.worldObj.isRemote) {
						player.addChatMessage(new ChatComponentText("5 Seconds left of WaterSealing."));
					}
				}
				if(watersealing == 50) {
					if(!event.player.worldObj.isRemote) {
						player.addChatMessage(new ChatComponentText("1 Second left of WaterSealing!"));
					}
				}
			}
		}

I'm using to check if the player is in contact with water. (I have an item that adds water sealing to the player via an item.)

If they have no water sealing they take damage, otherwise watersealing ticks down, when the sealing hits a certain level a chat message is sent to the player to warn them of imminent failure.

 

In this block:

		if(event.entity.isInWater()) {
			event.entity.setAir(10);
			if(Keyboard.isKeyDown(57)) {
				event.entity.motionY = 0.005D;
				if(event.entity.isCollidedHorizontally) {
					event.entity.motionY = 0.2D;
				}
			} else {
				event.entity.motionY = -1.0D;
			}
		} 

I'm modifying the players movement in water making them sink straight  to the bottom.

They can swim up but it's slow unless pressed up against a block for leverage.

 

And that's where the problem came from.

The server not liking the isKeyDown part.

 

 

I apologize for not knowing very much about packets and the like.

I'm new to java coding and I'm trying to learn through modding.

 

Also if it helps I have a github for this, it can be found here: https://github.com/Robo51/Newt

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

Posted

Would anyone be able to help with this?

I'm not trying to be rude it just comes out that way sometimes.

I'm here to try and learn as much as I can, won't you join me?

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

    • I know that this may be a basic question, but I am very new to modding. I am trying to have it so that I can create modified Vanilla loot tables that use a custom enchantment as a condition (i.e. enchantment present = item). However, I am having trouble trying to implement this; the LootItemRandomChanceWithEnchantedBonusCondition constructor needs a Holder<Enchantment> and I am unable to use the getOrThrow() method on the custom enchantment declared in my mod's enchantments class. Here is what I have so far in the GLM:   protected void start(HolderLookup.Provider registries) { HolderLookup.RegistryLookup<Enchantment> registrylookup = registries.lookupOrThrow(Registries.ENCHANTMENT); LootItemRandomChanceWithEnchantedBonusCondition lootItemRandomChanceWithEnchantedBonusCondition = new LootItemRandomChanceWithEnchantedBonusCondition(0.0f, LevelBasedValue.perLevel(0.07f), registrylookup.getOrThrow(*enchantment here*)); this.add("nebu_from_deepslate", new AddItemModifier(new LootItemCondition[]{ LootItemBlockStatePropertyCondition.hasBlockStateProperties(Blocks.DEEPSLATE).build(), LootItemRandomChanceCondition.randomChance(0.25f).build(), lootItemRandomChanceWithEnchantedBonusCondition }, OrichalcumItems.NEBU.get())); }   Inserting Enchantments.[vanilla enchantment here] actually works but trying to declare an enchantment from my custom enchantments class as [mod enchantment class].[custom enchantment] does not work even though they are both a ResourceKey and are registered in Registries.ENCHANTMENT. Basically, how would I go about making it so that a custom enchantment declared as a ResourceKey<Enchantment> of value ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.fromNamespaceAndPath([modid], [name])), declared in a seperate enchantments class, can be used in the LootItemRandomChanceWithEnchantedBonusCondition constructor as a Holder? I can't use getOrThrow() because there is no level or block entity/entity in the start() method and it is running as datagen. It's driving me nuts.
    • Hi here is an update. I was able to fix the code so my mod does not crash Minecraft. Please understand that I am new to modding but I honestly am having a hard time understanding how anyone can get this to work without having extensive programming and debugging experience as well as searching across the Internet, multiple gen AI bots (claude, grok, openai), and examining source code hidden in the gradle directory and in various github repositories. I guess I am wrong because clearly there are thousands of mods so maybe I am just a newbie. Ok, rant over, here is a step by step summary so others can save the 3 days it took me to figure this out.   1. First, I am using forge 54.1.0 and Minecraft 1.21.4 2. I am creating a mod to add a shotgun to Minecraft 3. After creating the mod and compiling it, I installed the .jar file to the proper directory in Minecraft and used 1.21.4-forge-54.1.0 4. The mod immediately crashed with the error: Caused by: java.lang.NullPointerException: Item id not set 5. Using the stack trace, I determined that the Exception was being thrown from the net.minecraft.world.item.Item.Properties class 6. It seems that there are no javadocs for this class, so I used IntelliJ which was able to provide a decompiled version of the class, which I then examined to see the source of the error. Side question: Are there javadocs? 7. This method, specifically, was the culprit: protected String effectiveDescriptionId() {      return this.descriptionId.get(Objects.requireNonNull(this.id, "Item id not set"));  } 8. Now my quest was to determine how to set this.id. Looking at the same source file, I determined there was another method:  public Item.Properties setId(ResourceKey<Item> pId) {             this.id = pId;             return this;   } 9. So now, I need to figure out how to call setId(). This required working backwards a bit. Starting from the constructor, I stubbed out the variable p which is of type Item.Properties public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); Rather than putting this all on one line, I split it up for readability like this: private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); Here is was the missing function, setId(), which takes a type of ResourceKey<Item>. My next problem is that due to the apparent lack of documentation (I tried searching the docs on this site) I could not determine the full import path to ResourceKey. I did some random searching on the Internet and stumbled across a Github repository which gave two clues: import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; Then I created the rk variable like this: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); And now putting it all together in order: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); This compiled and the mod no longer crashes. I still have more to do on it, but hopefully this will save someone hours. I welcome any feedback and if I missed some obvious modding resource or tutorial that has this information. If not, I might suggest we add it somewhere for people trying to write a mod that doesn't crash. Thank you !!!  
    • I will keep adding to this thread with more information in case anyone can help, or at the very least I can keep my troubleshooting organized. I decided to downgrade to 54.1.0 in the hopes that this would fix the issue but it didn't. At least now I am on a "recommended" version. The crash report did confirm my earlier post that the Exception is coming from effectiveDescriptionId(). I'll continue to see if I can find a way to set the ID manually.   Caused by: java.lang.NullPointerException: Item id not set         at java.base/java.util.Objects.requireNonNull(Objects.java:259) ~[?:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item$Properties.effectiveDescriptionId(Item.java:465) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item.<init>(Item.java:111) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ShotgunItem.<init>(ShotgunItem.java:19) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ModItems.lambda$static$0(ModItems.java:15) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/net.minecraftforge.registries.DeferredRegister$EventDispatcher.lambda$handleEvent      
    • It just randomly stop working after a rebooted my dedicated server PLEASE HELP!   com.google.gson   Failed to start the minecraft server com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $  
  • Topics

×
×
  • Create New...

Important Information

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