Jump to content

Recommended Posts

Posted (edited)

In my mod i'm registering different key bindings. However for some reason if i go to the Controls screen, to view the keys and re-map them, the game crashes with this error

java.lang.ArrayIndexOutOfBoundsException: 42
    at net.minecraft.client.gui.GuiKeyBindingList.<init>(GuiKeyBindingList.java:49)
    at net.minecraft.client.gui.GuiControls.initGui(GuiControls.java:38)
    at net.minecraft.client.gui.GuiScreen.setWorldAndResolution(GuiScreen.java:553)
    at net.minecraft.client.Minecraft.displayGuiScreen(Minecraft.java:1018)
    at net.minecraft.client.gui.GuiOptions.actionPerformed(GuiOptions.java:152)
    at net.minecraft.client.gui.GuiScreen.mouseClicked(GuiScreen.java:504)
    at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:619)
    at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:585)
    at net.minecraft.client.Minecraft.runTick(Minecraft.java:1797)
    at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1118)
    at net.minecraft.client.Minecraft.run(Minecraft.java:406)
    at net.minecraft.client.main.Main.main(Main.java:118)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
    at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
    at GradleStart.main(GradleStart.java:26)


These are my key bindings

	package com.rpg.core;
	import java.lang.reflect.Field;
	import org.lwjgl.input.Keyboard;
	import net.minecraft.client.settings.KeyBinding;
import net.minecraftforge.fml.client.registry.ClientRegistry;
	public class RPGKeys {
    public static KeyBinding KEY_GUILD;
    public static KeyBinding KEY_SHOP;
    public static KeyBinding KEY_SKILL_1;
    public static KeyBinding KEY_SKILLS;
    
    public static void addKeys() {
        KEY_GUILD = new KeyBinding("keys.guilds.description", Keyboard.KEY_G, "keys.guilds.category");
        KEY_SHOP = new KeyBinding("keys.shop.description", Keyboard.KEY_K, "keys.shop.category");
        KEY_SKILL_1 = new KeyBinding("keys.skills.description", Keyboard.KEY_Z, "keys.skills.category");
        KEY_SKILLS = new KeyBinding("keys.skills.skills", Keyboard.KEY_O, "keys.skills.category");
    }
    
    public static void registerKeys() {
        Field[] fields = RPGKeys.class.getFields();
        for (Field field : fields) {
            try {
                if (field.get(RPGKeys.class) != null) {
                    ClientRegistry.registerKeyBinding((KeyBinding) field.get(RPGKeys.class));
                }
            } catch (IllegalArgumentException | IllegalAccessException exception) {
                exception.printStackTrace();
            }
        }
    }
}

The keys works as intended, if i remove one key from here everything works fine. It's just in the Controls screen that the crash happen. What could be the cause of this? :/

Edited by JimiIT92

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Why are you accessing the fields in your class in such a weird way with your getFields() method? Why aren't you just registering your fields directly? You wrote more lines of code to loop through than you actually had key bindings...

 

I'm not sure if your weird way of registering the fields is causing the issue, but I would try making that more simple first.

 

After that, if there is still an issue, you'll notice that the error occurs where the listEntries array is going out of bounds. The size of that array is set based on the number of keybindings in the GameSettings plus the number of categories in KeyBinding together. So it may be an issue with the fact that you're creating new keybinding categories. I have done keybinding before but frankly I always used existing categories, but maybe there is some trick or maybe even bug. I would trace through the execution in debug mode in Eclipse by setting a breakpoint in that loop and watching how the index is advancing.

 

 

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

Posted

I've used new categories without problems.

 

		public static KeyBinding eventKey;

...
		eventKey = new KeyBinding("key." + Reference.MOD_ID + ".event", Keyboard.KEY_G, "key.categories." + Reference.MOD_ID);
		ClientRegistry.registerKeyBinding(eventKey);

 

Is addKeys() even called?  If so, you can register them at that point.

Posted

Yes, the addKeys is called (as i said the keys itself works normally, if i press one of them they do what they've been registered for). I used the reflection method because i plan to add more keybinds and with this i just have to register it, but i'll try to register them manually and see what happens :/

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Changing the registration method didn't solved the error, but i've noticed going into debug mode when i enter the controls screen that the key category for KEY_SKILLS and KEY_SKILL_1, wich is the same, is added twice in the key list, while other categories like normal vanilla keybinds categories are added once. Infact, if i move one of them to a new category everything works fine. This is how i register the key binds now

public static void registerKeys() {
        ClientRegistry.registerKeyBinding(KEY_GUILD);
        ClientRegistry.registerKeyBinding(KEY_SHOP);
        ClientRegistry.registerKeyBinding(KEY_SKILLS);
        ClientRegistry.registerKeyBinding(KEY_SKILL_1);
    }

So am i doing something wrong? And if so how can i register 2 keys within the same category? Or it's a Forge bug? 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Okay, if I look at the KeyBinding.KEYBIND_SET (which contains the categories) it turns out there is an add(category) called on that every time a key binding is constructed. Now technically a Java Set should not allow duplicates.

 

The category is a string, but the Java set properly uses the equals() method for comparison according to the "contract" of the method definition. 

 

So basically the category set will get the add() called every time, but it should not actually create any duplicates.

 

When tracing you should look at the contents of the KEYBIND_SET to see if there is actually an (apparent) duplicate added.

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

Posted

I've looked at the listEntries array that is generated (and that gives the exception). As you can see there are 2 categories with the same name but different ID, and these are the categories that comes from the 2 keys with the same one

 

Cattura.PNG

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Then you should trace through the constructor of the GuiKeyBindingList because it has a loop where it checks whether each category is unique before adding a category entry to listEntries.

 

Now that I look at it though, I think it is actually a bug. Because the way the loop works is that it has a "previous category" field called s and "current category" field called s1 and checks if they are equal. Basically it is checking if the category of the entry in this iteration of the loop is the same as the previous. However, that requires the categories to be sorted. There is a sort on the list, but I think maybe it is wrong because I think it is sorting the actual key bindings not necessarily just the categories.

 

Check if that is the problem.

 

So I think it might be a bug. You might be able to work around it by naming your keybinds so that the ones in the same category sort together, or something else that makes sure that the categories are processed consecutively.

 

Alternatively, you can override the whole gui by copying the class and fixing the bug and then handling the gui open event to put up your gui instead.

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

Posted

Actually the sort method looks suspicious generally. It is sorting an array of KeyBinding which are cast to Object and I don't think KeyBinding class has a comparator interface to help the sorting. So I don't think the sort would be meaningful in terms of the keybinding unless the default Object sort is based on toString() and even then seems a bit sketchy.

 

Basically when the loop starts you need to see whether the two entries with the same category are right after each other. If not then the loop will probably fail. Or just monitor the s and s1 values during the loop. There is definitely potential for something wrong there I think.

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

Posted

Yes in the first iteration the keys with the same category was not one after the other, meaning that when the game adds the second key the previous category was a different one and so it adds the duplicate. Fun fact: i've added more keys in the same category and now the controls screen appear and in the gui init the check on s and s1 is correct O_O 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted (edited)

This is how I registered my keybinds (https://github.com/KenLPham/ArcadeMod/blob/1.11.2/src/main/java/superhb/arcademod/util/KeyHandler.java) and I don't have any problems with it.

 

When do you call registerKeys? Copying your method worked, but I called registerKey in addKeys()

 

Also I think if you want to use your lang file for the Categories and Key Description you need to use I18n.format() (I'm guessing that's what you want to do considering what you've named it)

Edited by SuperHB
Posted

Me too has no problem with the keys itself, it's just that if i have exactly 2 keys of the same category (not more) the Controls screen crash the game. And it looks like it's a bug in the Control GUI, because it doesn't make any sense that more keys within the same category doesn't give the same crash

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted
24 minutes ago, JimiIT92 said:

Me too has no problem with the keys itself, it's just that if i have exactly 2 keys of the same category (not more) the Controls screen crash the game. And it looks like it's a bug in the Control GUI, because it doesn't make any sense that more keys within the same category doesn't give the same crash

I copied your code and was able to open the control screen. Where are you calling registerKey() though? I had it at the end of addKeys()

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

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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