Jump to content

[19.3] Example code.


Jontom Xire

Recommended Posts

I am new to Minecraft modding. The Forge documentation mentions there being examples of code somewhere, but I cannot find them. I also find the Forge documentation lacking in sufficient detail. There is a lot of implicit knowledge. After half a day of tearing my hair out, I finally managed to implement a simple key mapping of a single key.

 

To help new modders, I have provided a simple fully working code example of implementing a key mapping at https://gitlab.com/JontomXire/minecraft-forge-1.19.3-examples. I intend to extend this in due course with other examples. I would be grateful for all code examples that other people could provide. The criteria for acceptance are:

  1. Minimum code example. No need to include all the gradle stuff, just the build.gradle and anything under src. Try to base it on the example mod code included in the Forge MDK.
  2. Must build. All examples must be full and complete to compile without the user having to do anything other than copy in all the gradle stuff from the Forge MDK.
  3. Must run. All examples must work with nothing more than copying the JAR file into  the mods folder of a clean installation, and then running that installation. It must be possible for the user to see the mod working, even if that requires checking the debug.log file.

 

Edited by Jontom Xire
Fixed hyperlink.
Link to comment
Share on other sites

9 hours ago, Jontom Xire said:

I also find the Forge documentation lacking in sufficient detail.

Please provide a detailed explanation on what is lacking in the Forge documentation. I do strive to make it understandable for those who know how to program Java and provide references where it needs to be. As such, if something is lacking, I would like to know exactly what so that I may address and fix it.

Link to comment
Share on other sites

Before I start, I want to make it clear that I appreciate how much hard work has gone into the official Forge documentation. That said...

 

There are many examples where it is lacking in sufficient detail:

Import packages. Many symbols are used in multiple packages and searching the javadocs is painful. A list of all the relevant import packages referenced by an article, and which symbols mentioned in the article that each provides, would be really helpful.

At one point I was having an event listener ignored. The forge community wiki has an image at https://forge.gemwire.uk/images/c/cc/Guide_to_Event_Handlers.png that makes it very clear when the handler needs to be static and when not, as well as giving a comprehensive list of all the ways of registering event handlers. I spent hours figuring the same thing out based on the official Forge documentation and experimentation. The code fragments lack important context. Links to examples in each article would be really good.

The page on capabilities is very piecemeal. There is no overview of the process. There is a section on exposing and another on registering, but no clear explanation of how all the pieces fit together. By contrast the community wiki has an entire page with a detailed explanation of how capabilities work at https://forge.gemwire.uk/wiki/Capabilities.

No detail on parameters and return values for each function. I don't expect this for core Minecraft, or other 3rd party, classes and symbols, but adding doxygen to the Forge code would provide a very useful degree of documentation, with additional places to hold details of any gotchas or non-obvious details of functions, as well as links to the main documentation. Alternatively...

At https://nekoyue.github.io/ForgeJavaDocs-NG/javadoc/1.19.2/ there is a github repository for the Javadocs for Forge. They can be automatically generated using scripting etc. Why is it left to someone else to generate these documents? Why not have a simple script that will generate the documentation for each release and publish it. Then you can put a link to it in the official Forge documentation. More than that, each time you put a function name in the documentation, it could be a link to the relevant part of the javadocs.

A link to the community wiki, which as mentioned has many articles that are much better written, would be really helpful. It would also save you work. No point duplicating the effort.

As stated in my original post, the documentation, somewhere (I cannot find it again) mentioned that there were examples elsewhere. There are no links to these examples. The community wiki has such examples, E.g. https://forge.gemwire.uk/wiki/Capabilities#Code_Examples

 

It took me a whole day to figure out how to do a simple key mapping from the official forge documentation, and that was only possible after you responded (extremely promptly) to my bug report. Thank you for that. Even after the documentation was updated, it still took another half day. By comparison, at work I recently needed to work with Kvaser CAN devices, which I had never ever dealt with before. Instead of days, it took me about two hours to read and fully understand how to integrate their devices into our internal IO library. There are many many other examples of times when I have entered a programming task with no idea how to achieve it and had to rely on online documentation, and I have always found it much easier than I have found Minecraft modding. For someone new to modding, gaining the necessary information is extremely difficult.

 

Link to comment
Share on other sites

On the capabilities page:

Quote

Capabilities must be invalidated at the end of the provider’s lifecycle via LazyOptional#invalidate. For owned BlockEntities and Entities, the LazyOptional can be invalidated within #invalidateCaps. For non-owned providers, a runnable supplying the invalidation should be passed into AttachCapabilitiesEvent#addListener.

What is meant by an "owned" entity? What exactly makes an entity owned? Then it goes on to talk about a non-owned provider. A provider is not the same as an entity, surely?

Where do we put "invalidateCaps()"? Is this an interface API that we need to implement? How does it get called?

Edited by Jontom Xire
Link to comment
Share on other sites

An entity is a provider. A provider is anything that can have capabilities attached to it.

Quote

public abstract class Entity extends net.minecraftforge.common.capabilities.CapabilityProvider<Entity>

 

What it is talking about is a provider that has internal capabilites not registered in the CapabilityDispatcher - e.g. it overrides getCapability() instead.

See for example the internal inventory capabilities in LivingEntity (equipment slots) or BaseContainerBlockEntity (vanilla containers).

It has to override invalidateCaps() to invalidate these additional capabilities.

 

When you attach your capability to a say an entity, you can add that listener which will tell you when the entity has been removed from the world so you can do any additional tidyup

e.g. if your capability uses other internal capabilites that need to be invalidated.

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

9 minutes ago, warjort said:

An entity is a provider. A provider is anything that can have capabilities attached to it.

Technically a provider is a superset of that because not all providers will/have to fire the attach event.

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

In the community wiki example they implement a capability provider class that they then attach to the entity. In ElectroBlob's Wizardry mod which, even though it is based on 1.12.2, is what I am using as an example, they do much the same. So colour me confused.

My code is at https://gitlab.com/xire-mods/stats-and-skills/-/tree/daddy_development, in the PlayerData.java file. I have no idea how or where to invalidate the LazyOptional instance.

I am also currently wrestling with another problem, which is that when I handle the PlayerEvent.Clone event, there appear to be no capabilities attached to the Player instance returned by getOriginal(). I have added debug throughout to report hash codes, and when I call `player.getCapability(PLAYER_DATA_CAPABILITY)` on the Player returned by getOriginal(), it doesn't even call into my capability provider class.

    @SubscribeEvent
	public static void onPlayerCloneEvent(PlayerEvent.Clone event)
    {
        PlayerData new_data = PlayerData.get(event.getEntity());
        PlayerData old_data = PlayerData.get(event.getOriginal());
...
    }
...
    public static PlayerData get(Player player)
    {
        PlayerData                result;
        LazyOptional<PlayerData>  lo     = player.getCapability(PLAYER_DATA_CAPABILITY);

        if (lo.isPresent()) {
            StatsNSkills.LOGGER.info("JX: " + player.getName().getString() + " (" + player.hashCode() + ") has capability.");
            result = lo.resolve().get();
        } else {
            StatsNSkills.LOGGER.info("JX: " + player.getName().getString() + " (" + player.hashCode() + ") has no capability.");
            result = null;
        }

        return result;
	}
...
		public Provider(Player player)
        {
            data = new PlayerData(player);
            lazy_optional = LazyOptional.of(() -> data);
            StatsNSkills.LOGGER.info("JX: " + player.hashCode() + " = " + lazy_optional.hashCode());
		}

		@Override
		public <T> LazyOptional<T> getCapability(Capability<T> capability, Direction side)
        {
            if (capability == PLAYER_DATA_CAPABILITY)
            {
                StatsNSkills.LOGGER.info("JX: " + data.player.hashCode() + " -> " + lazy_optional.hashCode());
            }

            return PLAYER_DATA_CAPABILITY.orEmpty(capability, lazy_optional);
		}

The website has screwed up the indentation.

On login I get:

[30Dec2022 12:42:05.394] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: 240 = 642878468
[30Dec2022 12:42:05.655] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: 240 -> 642878468
[30Dec2022 12:42:05.655] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: JontomXire (240) has capability.
[30Dec2022 12:42:05.656] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: JontomXire has logged in 1 times.

The first line is when my Provider class is constructed. The second is from my Provider class' getCapabilities() function is called in the login event handler. The third line is from my get() function that gets the underlying data for the Player.

When I respawn I get the following debug output:

[30Dec2022 12:42:38.466] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: 513 = 372405745
[30Dec2022 12:42:38.467] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: 513 -> 372405745
[30Dec2022 12:42:38.467] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: JontomXire (513) has capability.
[30Dec2022 12:42:38.468] [Server thread/INFO] [org.xire.joko.stats_and_skills.StatsNSkills/]: JX: JontomXire (240) has no capability.

The first line is from construction of an instance of my Provider class when attaching to the new Player instance. The next two lines are from calling get() on the new Player instance in the player clone event handler. The third line is from calling get() on the original player instance. Before it I would expect to see a line of debug like the second line in the login debug. The lack of such a line shows that my getCapabilities() function in my Provider class is not being called.

 

Link to comment
Share on other sites

See the javadoc for CapabilityProvider.reviveCaps()

i.e. getting access to the original capabilities for removed objects.

Don't forget to invalidateCaps() afterwards.

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

7 hours ago, Jontom Xire said:

Import packages. Many symbols are used in multiple packages and searching the javadocs is painful. A list of all the relevant import packages referenced by an article, and which symbols mentioned in the article that each provides, would be really helpful.

We talked about doing this before using tooltips, but we couldn't figure out an efficient way to do so. The tooltips provided by materials don't work within code blocks, so the page would have to be more or less rewritten.

7 hours ago, Jontom Xire said:

At one point I was having an event listener ignored. The forge community wiki has an image at https://forge.gemwire.uk/images/c/cc/Guide_to_Event_Handlers.png that makes it very clear when the handler needs to be static and when not, as well as giving a comprehensive list of all the ways of registering event handlers. I spent hours figuring the same thing out based on the official Forge documentation and experimentation. The code fragments lack important context. Links to examples in each article would be really good.

I don't know why we have never included this image, it just never was mentioned before. It would be simple to add and reformat the doc.

7 hours ago, Jontom Xire said:

The page on capabilities is very piecemeal. There is no overview of the process. There is a section on exposing and another on registering, but no clear explanation of how all the pieces fit together. By contrast the community wiki has an entire page with a detailed explanation of how capabilities work at https://forge.gemwire.uk/wiki/Capabilities.

This was written before my time and I've only updated it. The one on fcw was written by Silk iirc. Though, it is on my list to update along with a few other pages.

7 hours ago, Jontom Xire said:

No detail on parameters and return values for each function. I don't expect this for core Minecraft, or other 3rd party, classes and symbols, but adding doxygen to the Forge code would provide a very useful degree of documentation, with additional places to hold details of any gotchas or non-obvious details of functions, as well as links to the main documentation. Alternatively...

At https://nekoyue.github.io/ForgeJavaDocs-NG/javadoc/1.19.2/ there is a github repository for the Javadocs for Forge. They can be automatically generated using scripting etc. Why is it left to someone else to generate these documents? Why not have a simple script that will generate the documentation for each release and publish it. Then you can put a link to it in the official Forge documentation. More than that, each time you put a function name in the documentation, it could be a link to the relevant part of the javadocs.

Uh yeah, this would never happen. The simple reason is that if you know how to use your IDE, it functions much better than posting and hosting the javadocs on the website. For a more technical reason, the Minecraft classes, methods, and fields would have no bearing on what they are called. They are obfuscated names to which could have any mapping applied on top of them, so it wouldn't be a reasonable thing to do.

Now, that's for the Forge classes. The Minecraft classes, method, and fields would be illegal to post, since it's against the license Mojang provides to us to use. So, the site you provided is illegal as it contains deobfuscated source of the game itself. If you would like documentation, you should go check out another of our mapping projects called Parchment which provides parameter names and javadocs that we have determined on top of the official mappings as Mojang does not provide them.

7 hours ago, Jontom Xire said:

A link to the community wiki, which as mentioned has many articles that are much better written, would be really helpful. It would also save you work. No point duplicating the effort.

I mean...they are both written by me at different points in time in most cases. Typically, I just update one and not the other depending on when people file issues since that's usually how I prioritize what I need to do. It doesn't save me any work since I'm the main contributor in both cases.

7 hours ago, Jontom Xire said:

As stated in my original post, the documentation, somewhere (I cannot find it again) mentioned that there were examples elsewhere. There are no links to these examples. The community wiki has such examples, E.g. https://forge.gemwire.uk/wiki/Capabilities#Code_Examples

The code examples should be integrated into the post itself, and the example is not the only way to do it. I don't like very specific code examples in most cases as people will tend to copy and paste code and expect it to work. This typically leads to people not understanding what they are doing and then getting mad when it doesn't work as they wanted it to. Code snippets are typically better when people need to infer the context based on previous topics and understand what is going on. Though, I could swing either way depending on how the examples are explained.

7 hours ago, Jontom Xire said:

There are many many other examples of times when I have entered a programming task with no idea how to achieve it and had to rely on online documentation, and I have always found it much easier than I have found Minecraft modding. For someone new to modding, gaining the necessary information is extremely difficult.

Personally, it would be great if you could open issues on the forge docs or on the FCW wiki tracker to let us know what is not explained well and what you would like to cover. I'm unfortunately not an omnipotent being, so I can only know when things are wrong when people tell me. I do try to address as many concerns as possible though since I do believe good documentation is the backbone for getting started on most projects. Though, I also believe that people who are trying to use Java, GLFW, Gradle, etc. should have prior knowledge as they are not incorporated with Forge itself and are commonly used throughout the environment.

I do wish eventually that there will be more than just me contributing major documentation actively instead of during the addition of a new process added by Forge or Mojang, but until then I will keep on improving and adding to what we have such that people such as yourself can learn how to mod, given the requisite java experience, and complain when there's something wrong so that it can be fixed.

Link to comment
Share on other sites

3 hours ago, ChampionAsh5357 said:

The code examples should be integrated into the post itself, and the example is not the only way to do it. I don't like very specific code examples in most cases as people will tend to copy and paste code and expect it to work. This typically leads to people not understanding what they are doing and then getting mad when it doesn't work as they wanted it to. Code snippets are typically better when people need to infer the context based on previous topics and understand what is going on. Though, I could swing either way depending on how the examples are explained.

That is actually one of my requirements for the docs to be "official" We can not have copy pasteable "example" code because the amount of people who will just copy pasta and not learn anything is stupid high. Everything *should* be psudo-code. This is an intentional design decision. Not to mention this allows you to not have to worry about mappings or anything that changes based on user setup.

 

11 hours ago, Jontom Xire said:

At https://nekoyue.github.io/ForgeJavaDocs-NG/javadoc/1.19.2/ there is a github repository for the Javadocs for Forge. They can be automatically generated using scripting etc. Why is it left to someone else to generate these documents? Why not have a simple script that will generate the documentation for each release and publish it. Then you can put a link to it in the official Forge documentation. More than that, each time you put a function name in the documentation, it could be a link to the relevant part of the javadocs.

As mentioned by Ash, this site is illegal due to Mojang's license. Combined with the entire concept of us publishing javadocs being useless because of obfuscation and mappings being a crowdsourced ever changing system. We used to publish javadoc archives. But decided to stop doing that when e got to 5TB of wasted space. As every build was around 20MB of just javadocs even compressed. And again, they were almost completely useless due do obfuscation of Mojang's code. Your IDE has great javadoc generation functionality. Hell you could build them yourself locally from the MDK. So it's not worth it to host publicly.

 

What you have to remember, is that you're modding a game that was never designed to be modded. We're hacking together a somewhat standard development interface for you using a lot of black magic in the backend to make things behave. As well as respect Mojang's wishes/copyrights.  Plus this is all being done by hobbyists/volunteers. We have reasons for doing everything the way do we it, including the docs.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

19 hours ago, ChampionAsh5357 said:

Personally, it would be great if you could open issues on the forge docs or on the FCW wiki tracker to let us know what is not explained well and what you would like to cover. I'm unfortunately not an omnipotent being, so I can only know when things are wrong when people tell me. I do try to address as many concerns as possible though since I do believe good documentation is the backbone for getting started on most projects. Though, I also believe that people who are trying to use Java, GLFW, Gradle, etc. should have prior knowledge as they are not incorporated with Forge itself and are commonly used throughout the environment.

I did open an issue about the key mapping previously, and was very impressed by your response and response time. I am hugely impressed if you are the only one trying to update two lots of documentation at once. Maybe reduce work by obsoleting one after ensuring the other has all relevant information.

I apologise for being lazy with raising issues since then, but the sheer amount of time it is taking me to get anything working means that the time I would need to spend actually thinking about where and how I am confused, and documenting it, would kill my project. I work full time as a software engineer and that means that normally I have no energy or inclination for hobby coding. This Christmas holidays is, as with previous years, the only time I can summon the energy to do any hobby coding.

 

Regarding examples, a working and compilable example gives context that allows you to understand the whole picture. At least it does for me. I know some people just copy and paste without taking the time to understand how things fit together. As far as I am concerned I am happy to let them simmer in the juices of their own incompetence. People like me who copy and paste bit by bit into my own code, refactoring completely as we go, while reading the documentation in parallel, will find working examples really useful.

 

Regarding the javadocs, I don't use IDEs. I can see how one would be useful here, but I used Eclipse previously and found it incredibly annoying. I hate wrestling with endless settings to get the indentation almost, but not completely, the way I like it. I hate the way IDEs try to write code for me, predicting what I am trying to type and just completely distracting my train of thought as I type. I have a vimrc file I have been using for almost 20 years, that I take from job to job and tweak as needed, and it gives me an editing environment that does exactly what I want. I guess I just need to spend some time to figure out how to generate the javadocs myself.

 

Once again, despite all my frustrations, I am really grateful to Ash and all of you for your work on Forge and the documentation.

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.