Jump to content

[1.15.2] Basic Question - Events and Capabilities


HalestormXV

Recommended Posts

So here is a relatively dumb question I am sure. I have multiple capabilities. 3 at the moment but only 2 are actually registered and being used the 3rd is completely disabled and not even registered as I am still working on it. When the time comes I want to Register correctly. As it is set-up now, each capability has an event handler (the stupid part that is probably wrong).

 

In each capabilities' EventHandler has the particular effects that I want that Capability to work with. So the AttachingEvent, an onDeath, and onAttack etc. However, I am noticing conflicts. I am getting crashes and it would seem that one Capability is trying to cast to another Capability despite there being ZERO like absolute ZERO reference to it anywhere in my code. This tells me that I am likely being foolish, and there should only be one Event handler that attaches all the capabilities at once and therefore only one 

MinecraftForge.EVENT_BUS.addListener(CAPABILITY HANDLER CLASS HERE::onAttachCapabilitiesEvent);
MinecraftForge.EVENT_BUS.addListener(CAPABILITY HANDLER CLASS HERE::onAttackEvent);

etc., etc. 

 

Here is the crash report. The reason why I know it isn't in that EffectRemeberance class is because once I disable my Explosium capability that for whatever reason is trying to cast itself everywhere the code works just fine, perfectly actually, (even my debug messages) and there is no issue. So this tells me I am doing something wrong when registering, and the only thing that "looks" off to me is all the separate registering I am doing and separate event handlers. 

 

If you need to see the Main where all the registering is happening here it is. It is currently a mess because I haven't cleaned and optimized it just yet: https://pastebin.com/YWKvtbdK

Edited by HalestormXV
Link to comment
Share on other sites

Don't register a capability with the event system, make a single class that handles ALL your events and then get the capability from the relevant object given to you by the event data.

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.

Link to comment
Share on other sites

1 hour ago, Draco18s said:

Don't register a capability with the event system, make a single class that handles ALL your events and then get the capability from the relevant object given to you by the event data.

Okay that can be done, so I just converted to the the above as you said. Everything is still working fine and my two capabilities are now registered in a CapabilityRegistration class and there is now a single CapabilityEventHandler class (so far with only onDeath subscribed to and onAttack subscribed to) but I am still getting the same error where it seems that the first capability to register is trying to cast to the other and as stated above, they dont even so much as reference each other anywhere in my code. And I am using the event to pull the data and check ifPresent. And as you will see below the Location Capability isn't even mentioned in here nor is it referenced.

 

public class CapabilityEventHandler {

    @SubscribeEvent
    public void onDeathEvent(LivingDeathEvent event) {
        Entity entity = event.getEntity();
        entity.getCapability(CapabilityEntityExplosium.ENTITY_EXPLOSIUM_CAPABILITY).ifPresent(h -> {
            int count = h.getCount();
            if (count > 0) {
                entity.getEntityWorld().createExplosion(entity, entity.getPosX(), entity.getPosY(), entity.getPosZ(), count * .3f + 1.0f, Explosion.Mode.DESTROY);
            }
        });
    }

    @SubscribeEvent
    public void onAttackEvent(AttackEntityEvent event) {
        Entity attacker = event.getEntity();
        if (attacker instanceof PlayerEntity){
            PlayerEntity player = (PlayerEntity) attacker;
            ItemStack stack = player.getHeldItemMainhand();
            if (stack.getItem() == ItemInitDeferred.EXPLOSIUM_DUST.get()) {
                Entity target = event.getTarget();
                target.getCapability(CapabilityEntityExplosium.ENTITY_EXPLOSIUM_CAPABILITY).ifPresent(handle ->
                {
                    int count = handle.getCount() + 1;
                    if (count < 9) {
                        handle.setCount(count);
                        player.sendStatusMessage(new TranslationTextComponent("message.increase_count", Integer.toString(count)), true);
                        stack.shrink(1);
                        player.setHeldItem(Hand.MAIN_HAND, stack);
                        event.setCanceled(true);
                        target.getEntityWorld().addParticle(ParticleTypes.FIREWORK, target.getPosX(), target.getPosY() + 1, target.getPosZ(), 0.0, 0.0, 0.0);
                    }else{
                        player.sendStatusMessage(new TranslationTextComponent("message.at_max_count", Integer.toString(count-1)), true);
                        event.setCanceled(true);
                    }
                });
            }
        }
    }
}
Edited by HalestormXV
Link to comment
Share on other sites

I don't know if you had noticed but I don't think this is where crash occurred.

  1. java.lang.ClassCastException: com.halestormxv.mysterium.data.explosium.DefaultEntityExplosium cannot be cast to com.halestormxv.mysterium.data.location.IEntityLocation
  2.     at net.minecraftforge.common.util.LazyOptional.ifPresent(LazyOptional.java:161) ~[?:?] {re:classloading}
  3.     at com.halestormxv.mysterium.effects.EffectRemembrance.savePosition(EffectRemembrance.java:91) ~[?:?] {re:classloading}
Link to comment
Share on other sites

The crash seems to be occurring somewhere in my (now) CapabilityEvent Handler. It is being triggered (the crash) when an Entity either getsAttacked or onDeath. For whatever reason my two capabilities seem to be conflicting. My ExplosiumCapability which is the only one that uses the attacked and onDeath, and my Location capability. The location capability is registered first in the MainClass, I took it off the Event_Bus.addListener like Draco suggested, and simply just now call a .register(CapabilityRegistration.class) it didn't make a difference.

 

For whatever reason whichever capability is registered first (now the location one) tries to cast itself to the capability registered second (now the explosium one) . My confusion is why. They in no way reference each other. the attack and the onDeath events don't even look for let alone pull data from anything except the Explosium capability as it should be. 

 

I know the code is somewhat correct, because if I disable the Explosium capability the Location one works just fine and does everything it is supposed to do. And if I disable the location capability the Explosium one works just fine and doesnt crash or anything and does exactly what it is supposed to. Which leads me back to my original point which is something is getting messed up perhaps when they registered?

Edited by HalestormXV
Link to comment
Share on other sites

Show your capability registration.

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.

Link to comment
Share on other sites

private void setup(final FMLCommonSetupEvent event)
{
    //CapabilityEntityLocation.register(); <---Disabled to make sure that they work when one is disabled and the other is enabled
    CapabilityEntityExplosium.register(); *****

    ComposterBlock.registerCompostable(0.7f, BlockInitDeferred.ARCANUM_LEAVES.get());
    DeferredWorkQueue.runLater(StructureGen::generateStructures);
    registerEntityWorldSpawns(ModEntityTypes.MOOPLE_ENTITY.get(), 89, 2, 4, BiomeInit.THE_MYSTIC_HILLS.get());
    registerEntityWorldSpawns(ModEntityTypes.NOVICE_CULTIST.get(), 72, 1, 4, BiomeInit.THE_LARVEN_FLATS.get(), BiomeInit.THE_MYSTIC_HILLS.get());
    PotionInit.addBrewingRecipes();
    MinecraftForge.EVENT_BUS.register(CapabilityRegistration.class); ********
    MinecraftForge.EVENT_BUS.addListener(WatchfulEyeBreakEvent::breakUnderWatch);
    MinecraftForge.EVENT_BUS.addListener(ChangedMagicTools::unequippedMagicTool);
    MinecraftForge.EVENT_BUS.register(CapabilityEventHandler.class);
}

Segment of Main ^

 

public class CapabilityRegistration {
    private static final ResourceLocation ENTITY_LOCATION_CAPABILITY = new ResourceLocation(MysteriumMain.MOD_ID, "locdata");
    private static final ResourceLocation ENTITY_EXPLOSIUM_CAPABILITY = new ResourceLocation(MysteriumMain.MOD_ID, "count");

    @SubscribeEvent
    public static void attachCapability(AttachCapabilitiesEvent<Entity> event){
        Entity entity = event.getObject();
        if (entity instanceof LivingEntity) {
            //event.addCapability(ENTITY_LOCATION_CAPABILITY, new EntityLocationProvider());
            event.addCapability(ENTITY_EXPLOSIUM_CAPABILITY, new EntityExplosiumProvider());
        }
    }
}

CapabilityRegistration/Attachment ^

 

public class CapabilityEntityLocation {
    @CapabilityInject(IEntityLocation.class)
    public static Capability<IEntityLocation> ENTITY_LOCATION_CAPABILITY = null;

    public static void register() {
        CapabilityManager.INSTANCE.register(IEntityLocation.class, new Storage(), DefaultEntityLocation::new);
    }

    public static class Storage implements Capability.IStorage<IEntityLocation> {

        @Nullable
        @Override
        public INBT writeNBT(Capability<IEntityLocation> capability, IEntityLocation instance, Direction side) {
            CompoundNBT tag = new CompoundNBT();
            tag.putDouble("xLoc", instance.getX());
            tag.putDouble("yLoc", instance.getY());
            tag.putDouble("zLoc", instance.getZ());
            tag.putInt("dimID", instance.getDimId());
            return tag;
        }

        @Override
        public void readNBT(Capability<IEntityLocation> capability, IEntityLocation instance, Direction side, INBT nbt) {
            double xLoc = ((CompoundNBT) nbt).getDouble("xLoc");
            double yLoc = ((CompoundNBT) nbt).getDouble("yLoc");
            double zLoc = ((CompoundNBT) nbt).getDouble("zLoc");
            int dimID = ((CompoundNBT) nbt).getInt("dimID");
            instance.setX(xLoc);
            instance.setY(yLoc);
            instance.setZ(zLoc);
            instance.setDim(dimID);
        }
    }
}

Initializing and Injecting the LocationCapability ^ (currently disabled in main)

 

public class CapabilityEntityExplosium {
    @CapabilityInject(IEntityExplosium.class)
    public static Capability<IEntityExplosium> ENTITY_EXPLOSIUM_CAPABILITY = null;

    public static void register() {
        CapabilityManager.INSTANCE.register(IEntityExplosium.class, new Storage(), DefaultEntityExplosium::new);
    }

    public static class Storage implements Capability.IStorage<IEntityExplosium> {

        @Nullable
        @Override
        public INBT writeNBT(Capability<IEntityExplosium> capability, IEntityExplosium instance, Direction side) {
            CompoundNBT tag = new CompoundNBT();
            tag.putInt("count", instance.getCount());
            return tag;
        }

        @Override
        public void readNBT(Capability<IEntityExplosium> capability, IEntityExplosium instance, Direction side, INBT nbt) {
            int count = ((CompoundNBT) nbt).getInt("count");
            instance.setCount(count);
        }
    }
}

Initlizing and Injecting the Explosium one ^ (currently is working with the disabling of the location one)

Link to comment
Share on other sites

Just giving this a bump as I solved it. @Draco18s thanks for the pointers and tips. I see why they were trying to cast to each other. My Provider, for each Capability never actually checked to ensure that the Capability was in fact the Capability it has to cast to.

 

I am guessing it has something to do with Lazy (i am very new to Lazy to be quite honest so it doesnt surprise me that my error was in there).

 

For reference I built my Capability system similar to how McJty built his in the tutorial videos for 1.15.2 and also took bigger aspects like registration and what not from TheGreyGhost. After looking at TheGreyGhosts capability code as a whole (https://github.com/TheGreyGhost/MinecraftByExample/tree/1-15-2-working/src/main/java/minecraftbyexample/mbe65_capability) I see that he actually enforces a check in his providers, the step I missed above. Which would make sense why when one was disabled it wouldn't conflict because there wasn't anything to conflict with when setting up the getCapability. 

 

My question now, because as I have repeatedly said, I am not just in it for answers, i want to actually learn the workings of it because that makes you a better coder and helps enhance your coding knowledge, is WHY that check is needed? If these are entirely separate classes that don't even reference/link to each other, why does that check still need to be made. Does it in fact have to do with how Lazy works?

Edited by HalestormXV
Link to comment
Share on other sites

43 minutes ago, HalestormXV said:

Does it in fact have to do with how Lazy works?

No, it has to do with how anything works. Imagine you were trying to cast an instance of Object A to Object B (let's say Object A is LivingEntity and Object B is PlayerEntity). We cannot blindly cast one to the other as not all LivingEntities are PlayerEntities. Therefore, we have to insure that the entity is a player before casting using some sort of check. The same is true for an Optional. We have to verify that the instance our optional is holding is the same of that we are trying to grab. Since we cannot compare generics directly , the next best thing is to compare the two singleton capabilities itself as they are directly related to the instance they are holding. A lazy just defers initializing or calling a specific object until first access and then stores the result. It provides a way to grab a pointer to an instance of an object before it might be specifically registered. Lazily using objects is quite prevalent in Java itself as can be seen with how Streams work.

Link to comment
Share on other sites

1 hour ago, ChampionAsh5357 said:

No, it has to do with how anything works. Imagine you were trying to cast an instance of Object A to Object B (let's say Object A is LivingEntity and Object B is PlayerEntity). We cannot blindly cast one to the other as not all LivingEntities are PlayerEntities. Therefore, we have to insure that the entity is a player before casting using some sort of check. The same is true for an Optional. We have to verify that the instance our optional is holding is the same of that we are trying to grab. Since we cannot compare generics directly , the next best thing is to compare the two singleton capabilities itself as they are directly related to the instance they are holding. A lazy just defers initializing or calling a specific object until first access and then stores the result. It provides a way to grab a pointer to an instance of an object before it might be specifically registered. Lazily using objects is quite prevalent in Java itself as can be seen with how Streams work.

 

That actually makes it clear. I haven't seen Lazy before until about 3 weeks ago when I stepped back into the Modding world. I also haven't worked with Minecraft modding since 1.12.2 and it was very brief. Prior to that it was only 1.7.10. Also I don't code with Java normally so that works against me, only when working on Minecraft Mods am I utilizing Java so it is somewhat "unfamiliar" to me after not working with it for such a period of time, so when I see something like "Lazy" i get all Huh o_O. But that explanation does make sense so I thank you.

Link to comment
Share on other sites

LazyOptional is just a lazily-instantiated Optional.

It still holds a value, that value is of type T. T as in LazyOptional<T>

That value gets cast to T when you call someLazy.cast(), so if the value actually stored in the LazyOptional is not actually of type T, the cast will fail.

 

Optionals are just a box wrapped around a concrete object because you don't know if that object actually exists or not (which is why you have to use ifPresent, orElse, or orElseThrow--depending on circumstances).

Edited by Draco18s

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.

Link to comment
Share on other sites

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.

×
×
  • Create New...

Important Information

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