Jump to content

[1.12.2] Soft Dependency on Capabilities


Cadiboo

Recommended Posts

Is it possible to use has/getCapability with capabilities that may or may not exist (provided by mods that may or may not be inatalled). For example if your building an energy/electricity based mod & want to support but not require RedstoneFlux & Tesla.

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Exactly what I was looking for, however how would I use it specifically with capabilities? (Its @Optional.Method seems to be the way to go, but I can’t think of how, Also it seems to have been made before capabilities were with the @Optional.Interface)

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

As far as I am aware the Optional is going away in 1.13. 

CapabilityInject will not insert a capability that isn't provided thus it will stay null. But that kinda requires a hard dependency anyway since you need to pass a class to the annotation.

My solution would be to put all interaction with potentially present capabilities in a separate class and don't load or ever touch it if the mod providing the capability isn't present.

Link to comment
Share on other sites

So I can’t do something like getCapabilityIfExists(CapabilityWhatever.WHATEVER). (Obviously)

Can I do something like

Capability<> potentialCapability = CapabilityRegistry.getCapability(ResourceLocation CapabilityID);

if(potentialCapability!=null){

Object actualCapability = world/entity/tileEntity/player.getCapability(potentialCapability);

}

 

...while writing that out I realised how useless having an Object that you can’t cast would be (I guess you could use reflection to find methods & invoke them)

 

All this looks like the answer is StringlyTyped code & a bunch of reflection.

 

38 minutes ago, V0idWa1k3r said:

My solution would be to put all interaction with potentially present capabilities in a separate class and don't load or ever touch it if the mod providing the capability isn't present.

How would I use it then? (And more importantly how would I avoid loading it)

How would I do something like WhateverCapability capabilityInstance = getWhateverCapabilityIfItExists()

when I can’t

a) import WhateverCapability (it will crash if it doesn’t exist) 

b) make a variable of type WhateverCapability (same reason as a)

c) invoke getWhateverCapabilityIfItExists (Loading the class with crash if it doesn’t exist)

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

27 minutes ago, Cadiboo said:

How would I do something like WhateverCapability capabilityInstance = getWhateverCapabilityIfItExists()

 when I can’t

a) import WhateverCapability (it will crash if it doesn’t exist) 

b) make a variable of type WhateverCapability (same reason as a)

In said class you can do whatever you want to do. The class would be dependent on the api provided by another mod.

27 minutes ago, Cadiboo said:

c) invoke getWhateverCapabilityIfItExists (Loading the class with crash if it doesn’t exist)

Don't invoke that method unless Loader.isModLoaded returns true. Then the class will not be loaded.

If you really need to interact with the capability in an another class you can create a wrapper.

Example scenario:

// Depends on ModA
class EnergyCapabilityA
{
	int getEnergy();

	void setEnergy(int i);
}

// Depends on ModB
class EnergyCapabilityB
{
	int getEnergy();

	void addEnergy(int i);

	void removeEnergy(int i);
}

// Your generic wrapper interface, depends on nothing
interface ICompatibleEnergyCapability
{
	int getEnergy();

	void addEnergy(int i);

	void remEnergy(int i);
}

// Your wrapper provider interface, depends on nothing
interface ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side);
}

// A custom caster class, depends on ModA
class AWrapperProvider implements ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side)
	{
		EnergyCapabilityA capA = tile.getCapability(A_ENERGY_CAP, side)
		return new ICompatibleEnergyCapability(){
			int getEnergy(){ return capA.getEnergy(); }
			void addEnergy(int i){ capA.setEnergy(capA.getEnergy() + i); }
			void remEnergy(int i){ capA.setEnergy(capA.getEnergy() - i); }
		}
	}
}

// A custom caster class, depends on ModB
class BWrapperProvider implements ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side)
	{
		EnergyCapabilityB capB = tile.getCapability(B_ENERGY_CAP, side)
		return new ICompatibleEnergyCapability(){
			int getEnergy(){ return capB.getEnergy(); }
			void addEnergy(int i){ capB.addEnergy(i); }
			void remEnergy(int i){ capB.removeEnergy(i); }
		}
	}
}

// A class containing the wrappers, depends on nothing
class WrappersHolder
{
	ICECProvider wrapperA;
	ICECProvider wrapperB;

	void init()
	{
		if (Loader.isModLoaded("ModA"))
		{
			wrapperA = Class.forName("AWrapperProvider").newInstance();
		}

		if (Loader.isModLoaded("ModB"))
		{
			wrapperB = Class.forName("BWrapperProvider").newInstance();
		}
	}
}

// Whenever you need to work with your capabilities
class Worker
{
	void work()
	{
		if (WrappersHolder.instance.wrapperA != null)
		{
			ICECProvider aProvider = WrappersHolder.instance.wrapperA;
			ICompatibleEnergyCapability cap = aProvider.createWrapper(tile, side);
			// Do whatever you want with the cap
		}
	}
}

This way everything is safe to use since you are not loading any classes that may or may not be present and everything is nicely in a wrapper and bound to a common interface.

 

If you want your has/getCapability to return a capability of an another mod it is a bit more tricky but also possible by creating wrappers for those capabilities in a similar way that depend on your main capability, only constructing them if the mod is present and returning them. You would need to be careful with your Capability<> instances though.

Edited by V0idWa1k3r
Link to comment
Share on other sites

13 minutes ago, V0idWa1k3r said:

In said class you can do whatever you want to do. The class would be dependent on the api provided by another mod.

Don't invoke that method unless Loader.isModLoaded returns true. Then the class will not be loaded.

If you really need to interact with the capability in an another class you can create a wrapper.

Example scenario:


// Depends on ModA
class EnergyCapabilityA
{
	int getEnergy();

	void setEnergy(int i);
}

// Depends on ModB
class EnergyCapabilityB
{
	int getEnergy();

	void addEnergy(int i);

	void removeEnergy(int i);
}

// Your generic wrapper interface, depends on nothing
interface ICompatibleEnergyCapability
{
	int getEnergy();

	void addEnergy(int i);

	void remEnergy(int i);
}

// Your wrapper provider interface, depends on nothing
interface ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side);
}

// A custom caster class, depends on ModA
class AWrapperProvider implements ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side)
	{
		EnergyCapabilityA capA = tile.getCapability(A_ENERGY_CAP, side)
		return new ICompatibleEnergyCapability(){
			int getEnergy(){ return capA.getEnergy(); }
			void addEnergy(int i){ capA.setEnergy(capA.getEnergy() + i); }
			void remEnergy(int i){ capA.setEnergy(capA.getEnergy() - i); }
		}
	}
}

// A custom caster class, depends on ModB
class BWrapperProvider implements ICECProvider
{
	ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side)
	{
		EnergyCapabilityB capB = tile.getCapability(B_ENERGY_CAP, side)
		return new ICompatibleEnergyCapability(){
			int getEnergy(){ return capB.getEnergy(); }
			void addEnergy(int i){ capB.addEnergy(i); }
			void remEnergy(int i){ capB.removeEnergy(i); }
		}
	}
}

// A class containing the wrappers, depends on nothing
class WrappersHolder
{
	ICECProvider wrapperA;
	ICECProvider wrapperB;

	void init()
	{
		if (Loader.isModLoaded("ModA"))
		{
			wrapperA = Class.forName("AWrapperProvider").newInstance();
		}

		if (Loader.isModLoaded("ModB"))
		{
			wrapperB = Class.forName("BWrapperProvider").newInstance();
		}
	}
}

// Whenever you need to work with your capabilities
class Worker
{
	void work()
	{
		if (WrappersHolder.instance.wrapperA != null)
		{
			ICECProvider aProvider = WrappersHolder.instance.wrapperA;
			ICompatibleEnergyCapability cap = aProvider.createWrapper(tile, side);
			// Do whatever you want with the cap
		}
	}
}

This way everything is safe to use since you are not loading any classes that may or may not be present and everything is nicely in a wrapper and bound to a common interface.

 

If you want your has/getCapability to return a capability of an another mod it is a bit more tricky but also possible by creating wrappers for those capabilities in a similar way that depend on your main capability, only constructing them if the mod is present and returning them. You would need to be careful with your Capability<> instances though.

Wait, Not sure I understand, (basic java)

will this code load the class (I always assumed it would)?

if(false){

loadClass()

}

 

Cause if it will never load the class, then half the problems solved.

However, could you provide an example of how this code would be written if the capability doesn’t exist?

//inside tile entity update method

for(facing : EnumFacing.VALUES) {  pos.offset(facing).getTile....getCapability(CapabilityEnergy.ENERGY)....transferEnergyTo(); // this should always work (let’s assume that there is a tile at the pos and bag this tile has the capability)

 

pos.offset(facing).getTile....getCapability(CapabilityTeslaEnergy.TESLA_ENERGY)....transferEnergyTo(); // how should this code be written?

 

}

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

21 minutes ago, Cadiboo said:

will this code load the class (I always assumed it would)?

if(false){

loadClass()

}

By default it won't. Classes are loaded on demand. However I do not really trust this since I do not know if this is a required specification of the JVM behaviour or not. If it isn't a custom JVM provided by a third-pary may indeed load the class. If someone could link me to a specifications for this case it would be great.

 

23 minutes ago, Cadiboo said:

However, could you provide an example of how this code would be written if the capability doesn’t exist?

//inside tile entity update method

for(facing : EnumFacing.VALUES) {  pos.offset(facing).getTile....getCapability(CapabilityEnergy.ENERGY)....transferEnergyTo(); // this should always work (let’s assume that there is a tile at the pos and bag this tile has the capability)

 

pos.offset(facing).getTile....getCapability(CapabilityTeslaEnergy.TESLA_ENERGY)....transferEnergyTo(); // how should this code be written?

// In your TE
for (EnumFacing side : EnumFacing.values())
{
    TileEntity tile = this.world.getTileEntity(this.pos.offset(side));
    if (tile != null)
    {
        EnergyCapability energy = tile.getCapability(CapabilityEnergy.ENERGY, side.opposite());

        // This if/else construct is very important because other mods may use a different energy API but still expose their energy capability as a forge energy one via a wrapper. In fact as far as I am aware all of them do. However in case they don't the else goes into play.
        if (energy != null)
        {
            energy.transferEnergyTo(...);
        }
        else
        {
            if (WrappersHolder.instance.teslaWrapper != null)
            {
                ICECProvider teslaProvider = WrappersHolder.instance.teslaWrapper;
                ICompatibleEnergyCapability cap = teslaProvider.createWrapper(tile, side);
                if (cap != null)
                {
                    cap.transferEnergyTo(...);
                }
                else
                {
                    ...
                }
            }
        }
    }
}

// The wrapper creation
class TeslaWrapperProvider implements ICECProvider
{
  	// Since this class is loaded only if the mod is loaded it is completely safe to reference that mod in it.
    @CapabilityInject(Tesla.class)
    static final Capability<Tesla> TESLA_CAP = null;
    
    ICompatibleEnergyCapability createWrapper(TileEntity tile, EnumFacing side)
    {
        Tesla cap = tile.getCapability(TESLA_CAP, side);
        if (cap == null)
        {
            return null;
        }
        
        return new ICompatibleEnergyCapability(){
            ...
            
            void transferEnergyTo(...) { cap.transferEnergyTo(...); }
        }
    }
}

 

  • Like 1
Link to comment
Share on other sites

There's a way to make your own proxy system that depends on what mods are loaded instead of which side you're on. I have a tutorial here: http://jabelarminecraft.blogspot.com/p/minecraft-modding-system-for-optional.html

 

The basic idea is that you would have methods that in case that a certain mod is loaded would do one thing (which is safe to reference that mod's classes) and otherwise do nothing (i.e. not referencing unloaded classes).

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

Link to comment
Share on other sites

16 minutes ago, jabelar said:

There's a way to make your own proxy system that depends on what mods are loaded instead of which side you're on. I have a tutorial here: http://jabelarminecraft.blogspot.com/p/minecraft-modding-system-for-optional.html

That is pretty much what I am already suggesting. The wrapper factories will be null if the mod the factory provides a wrapper for isn't loaded. There is no reason to default to an empty method instead. You can simply check for null. Your approach is also valid though.

Link to comment
Share on other sites

2 minutes ago, V0idWa1k3r said:

That is pretty much what I am already suggesting. The wrapper factories will be null if the mod the factory provides a wrapper for isn't loaded. There is no reason to default to an empty method instead. You can simply check for null. Your approach is also valid though.

Yeah, same idea.

 

But I don't know that it is safe to assume generally assume a class isn't loaded unless a method from it is invoked. It depends on the class loader implementation and possibly the JVM implementation. Some class loaders do actually do lazy loading in advance, not sure about Minecraft's. There is also technically a difference between loading and initializing a class, so need to be careful when reading info about "loading" as some information on the web is confusing the two -- initialization definitely happens when first access to a symbol in a class happens, but loading may happen before. Lastly, even if you can trust that class isn't loaded until used you have to be careful that there are no other initializations or static references as well that might trigger it -- for example, I don't know but I suspect that having annotations like client-only annotation might cause loading. Someone with more knowledge of the Minecraft class loader would have to weigh in...

 

If we could rely on classes only loading when referenced then we wouldn't really need the client proxy system...

 

I think explicitly handling the mod loading in pre-init like in the tutorial is more generally "safe".

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

Link to comment
Share on other sites

4 minutes ago, jabelar said:

But I don't know that it is safe to assume generally assume a class isn't loaded unless a method from it is invoked. It depends on the class loader implementation and possibly the JVM implementation. Some class loaders do actually do lazy loading in advance, not sure about Minecraft's.

Yeah, exactly my point. That's why I propose all this system in the first place and even use reflection to instantinate "dangerous" classes - to not allow loading unless I want it to happen.

 

5 minutes ago, jabelar said:

I don't know but I suspect that having annotations like client-only annotation might cause loading. Someone with more knowledge of the Minecraft class loader would have to weigh in...

Those annotations are simply an indicator to the class transformer to strip the class of those methods as it is loaded via ASM. It doesn't cause class loading on it's own. As far as I am aware the only two annotations that cause class loading are @Mod and @EventBusSubscriber.

 

8 minutes ago, jabelar said:

I think explicitly handling the mod loading in pre-init like in the tutorial is more generally "safe".

My proposal relies on the init method being called at some point, preferrably at pre-init too. So what we are suggesting is essentially the same thing, I just choose to represent the mod not loaded scenario with a null instead of a dummy implementation.

Link to comment
Share on other sites

Thanks so much, this is very very helpful!

 

12 hours ago, V0idWa1k3r said:

if (WrappersHolder.instance.teslaWrapper != null)

Could you explain this a little more? I assume it basically checks if the mod is loaded (and returns null if it isn’t), but I don’t see how or understand the naming

 

Regarding loading/initialising, I’ve seen in the Forge code somewhere that they use Sun’s classloading methods to (I think) “load” pretty much every class without initialising or actually loading it.

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

8 hours ago, Cadiboo said:

Could you explain this a little more? I assume it basically checks if the mod is loaded (and returns null if it isn’t), but I don’t see how or understand the naming

Basically it all goes down to the initial init method

class WrappersHolder
{
	ICECProvider wrapperA;
	ICECProvider wrapperB;

	void init()
	{
		if (Loader.isModLoaded("ModA"))
		{
			wrapperA = Class.forName("AWrapperProvider").newInstance();
		}

		if (Loader.isModLoaded("ModB"))
		{
			wrapperB = Class.forName("BWrapperProvider").newInstance();
		}
	}
}

By default the fields contain a null value. The init method checks if the mod is loaded and if it is assigns a new wrapper object to that field. If the mod isn't loaded then no wrapper is assigned and the field stays null. So checking for null is then the same as checking for whether the mod is loaded or not - if it is then the field won't be null. 

As for the naming, well you can name the fields whatever you'd like. The instance in that line is there because these fields aren't static and thus need an instance to access them. The instance is just a field declared perhaps like this:

public static WrappersHolder instance = new WrappersHolder();

It is not needed, you can instead make the fields that hold the wrappers static and then you don't need an instance to access them.

 

Edit: as for the class loading I would imagine forge scans the packages to find either more packages to scan or .class files. It then would read those .class files as a binary stream and convert them to bytecode using the ASM library. Then it is trivial to analyze that bytecode to find an annotation. If an annotation is present then the class name/path is stored somewhere to load at a later date. The bytecode is then discarded. This way the classes are never loaded since they only exist for a short amount of time as bytecode in memory, not as actual loaded classes. 

Edited by V0idWa1k3r
  • Like 1
Link to comment
Share on other sites

Ok, I think that I get the general idea thanks so much. I’ve got a couple simple questions that I’ll solve with some shotgun debugging tomorrow.

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

2 hours ago, diesieben07 said:

I recommend a system similar to @SidedProxy. Have an interface that describes your behavior and then two implementations, one for when the capability exists and one for when it does not.

@CapabilityInject can be put on a method, which can then replace your default ("capability not present") implementation with one that references the capability.

Yep, pretty much what I was suggesting, since I've seen you suggest it in the past.

Edited by jabelar

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

Link to comment
Share on other sites

How would I use Events from another mod that may or may not be installed?

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

43 minutes ago, jabelar said:

the other mod has created custom events (i.e. extended the Event class) and is posting them

Yeah, to the EVENT_BUS

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

3 minutes ago, NolValue said:

So basically, you want to check if there is a capability then execute code correct?

I'm fairly certain you can do something along the lines of using

if [Entity].hasCapability([CapabilityName]){ACTIONHERE} or to a similar effect

I'm not sure if the capability exists (The mod that provides the capability may not be installed). 

Im planning on following the advice I've been given so far and basically do what @SidedProxy does, but for the capabilities (with a bunch more wrappers probably)

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

2 minutes ago, NolValue said:

In that case, that code should still work as hasCapability checks if it's there or not.


If it isn't there, use an else statement to have it send a chat message stating that the player needs to install that mod

The code will crash if the mod isn't loaded, seeing as how he would be getting the Capability from one of that mods classes, this post was so that he could find a way around that. And he has, but now he needs to know if an Event exists which as far as I know he will have to do something like Loader.isModLoaded and then only do his registration that way.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

1 minute ago, Animefan8888 said:

The code will crash if the mod isn't loaded, seeing as how he would be getting the Capability from one of that mods classes, this post was so that he could find a way around that. And he has, but now he needs to know if an Event exists which as far as I know he will have to do something like Loader.isModLoaded and then only do his registration that way.

That wouldn't crash the code, hasCapability functions like a boolean from what I believe.

 

Anyways, I'm not sure if there is any way to check if an event is there either. He'd just have to check if that other mod is there or not as you said.

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.

Announcements



×
×
  • Create New...

Important Information

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