Difference between revisions of "Proper Mod Structuring"

From Forge Community Wiki
m
 
(17 intermediate revisions by 7 users not shown)
Line 1: Line 1:
  
== Structuring Your Mod ==
+
The structure of your mod is important in keeping your mod organized, for both your benefit and the benefit of anyone who wishes to make a feature or an
 +
add-on for your mod. A disorganized mod structure may cause headaches when someone is trying to update it to a higher version, especially if they cannot modify the package structure, due to i.e. licensing.
  
We’ll look at how to organize your mod into different files and what those files should do.
+
{{Tip|Note that this page is only a recommendation for your mod structure; you may structure your mod in any way you see fit.}}
=== Packaging ===
 
  
Pick a unique package name. If you own a URL associated with your project, you can use it as your top level package. For example if you own “example.com”, you may use <code>com.example</code> as your top level package.
+
== Packaging ==
  
{{Colored box|title=Important|content=If you do not own a domain, do not use it for your top level package. It is perfectly acceptable to start your package with anything, such as your name/nickname, or the name of the mod.}}
+
Pick a unique package name for your mod. If you own a URL associated with your project, you can use it as your top level package. For example if you own "example.com", you may use <code>com.example</code> as your top level package.  
 +
The next level package is usually your mod's ID: if your mod ID is <code>examplemod</code>, your mod's package will be <code>com.example.examplemod</code>.
  
After the top level package (if you have one) you append a unique name for your mod, such as <code>examplemod</code>. In our case it will end up as <code>com.example.examplemod</code>.
+
{{Tip/Important|Do not use a domain for your top level package if you do not own that domain. It is perfectly acceptable to have your top level package be named with anything, such as your name/nickname, or the name of the mod (<code>me.exampledude.examplemod</code>).}}
  
=== The <code>mods.toml</code> file ===
+
=== Using Subpackages ===
  
This file defines the metadata of your mod. Its information may be viewed by users from the main screen of the game through the Mods button. A single info file can describe several mods.
+
Rather than clutter up a single class and package with everything, it is recommended you break your mod into subpackages. Below are a few possible methods for laying out your folder structure though you may as well come up with your own layout too.
  
The <code>mods.toml</code> file is formatted as [https://github.com/toml-lang/toml TOML], the example mods.toml file in the MDK provides comments explaining the contents of the file. It should be stored as <code>src/main/resources/META-INF/mods.toml</code>. A basic <code>mods.toml</code>, describing one mod, may look like this:
+
* '''Group by Logic''': One possible strategy is to make subpackages for logical units of your mod. For example, if you have a block called Super Furnace you would put its block, its block entity and its item all under <code>feature/superfurnace</code>.
<syntaxhighlight lang="toml">
+
* '''Group by Function''': Another common strategy is to make subpackages for grouping classes that have a common purpose. For example, your blocks classes can be under <code>blocks</code>, your entities classes can be under <code>entities</code>, your helper utilities can be under <code>helpers</code>, etc.
  
    # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
+
No matter how your final structure looks like it is highly recommended to add a <code>client</code> subpackage under your main package. This helps to isolate your [[Sides|client-only code]] from the rest, such as your Screens and renderers.
    modLoader="javafml"
 
    # A version range to match for said mod loader - for regular FML @Mod it will be the forge version
 
    # Forge for 1.15.2 is version 31
 
    loaderVersion="[31,)"
 
    # A URL to refer people to when problems occur with this mod
 
    issueTrackerURL="github.com/MinecraftForge/MinecraftForge/issues"
 
    # If the mods defined in this file should show as seperate resource packs
 
    showAsResourcePack=false
 
    [[mods]]
 
      modId="examplemod"
 
      version="1.0.0.0"
 
      displayName="Example Mod"
 
      updateJSONURL="minecraftforge.net/versions.json"
 
      displayURL="minecraftforge.net"
 
      logoFile="assets/examplemod/textures/logo.png"
 
      credits="I'd like to thank my mother and father."
 
      authors="Author"
 
      description='''
 
      Lets you craft dirt into diamonds. This is a traditional mod that has existed for eons. It is ancient. The holy Notch created it. Jeb rainbowfied it. Dinnerbone made it upside down. Etc.
 
      '''
 
  
      [[dependencies.examplemod]]
+
By keeping your code in clean subpackages, you can grow your mod much more organically.
        modId="forge"
 
        mandatory=true
 
        versionRange="[31,)"
 
        ordering="NONE"
 
        side="BOTH"
 
  
      [[dependencies.examplemod]]
+
{{Tip|If you are unsure on how exactly you want to structure your mod it can be a good idea to look at the source of others to see how they did it.}}
        modId="minecraft"
 
        mandatory=true
 
        versionRange="[1.15.2]"
 
        ordering="NONE"
 
        side="BOTH"
 
</syntaxhighlight>
 
  
The default Gradle configuration replaces <code>${file.jarVersion}</code> with the project version, but ''only'' within <code>mods.toml</code>, so you should use those instead of directly writing them out. Here is a table of attributes that may be given to a mod, where <code>mandatory</code> means there is no default and the absence of the property causes an error.
+
== Class Naming Schemes ==
  
{| class="wikitable"
+
A common class naming scheme allows easier deciphering of the purpose of the class, and makes it easier for someone developing for your mod to find specific classes.
|-
 
! Property      !! Type    !! Default      !! Description
 
|-
 
| modid        || string  || mandatory    || The modid this file is linked to.
 
|-
 
| version      || string  || mandatory    || The version of the mod. It should be just numbers seperated by dots, ideally conforming to Semantic Versioning.
 
|-
 
| license      || string  || mandatory    || The license that the mod is held under
 
|-
 
| displayName  || string  || mandatory    || The user-friendly name of this mod.
 
|-
 
| updateJSONURL || string  || ""          || The URL to a version JSON.
 
|-
 
| displayURL    || string  || ""   || A link to the mod’s homepage.
 
|-
 
| logoFile || string  || ""   || The filename of the mod’s logo. It must be placed in the root resource folder, not in a subfolder.
 
|-
 
| credits || string  || ""   || A string that contains any acknowledgements you want to mention.
 
|-
 
| authors || string  || ""   || The authors to this mod.
 
|-
 
| description || string  || mandatory    || A description of this mod.
 
|-
 
| dependencies || [list]  || []   || A list of dependencies of this mod.
 
|}
 
  
NOTE:  All version ranges use the [https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html Maven Version Range Specification].
+
The usual style is to use suffixes for your classes, for example:
  
=== The Mod File ===
+
* An <code>Item</code> called <code>PowerRing</code> would have a class name of <code>PowerRingItem</code>.
 +
* A <code>Block</code> called <code>NotDirt</code> would have a class name of <code>NotDirtBlock</code>.
 +
* A <code>BlockEntity</code> for a block called <code>SuperChewer</code> would have a class name of <code>SuperChewerBlockEntity</code>.
  
Generally, we’ll start with a file named after your mod, and put into your package. This is the ''entry point'' to your mod and will contain some special indicators marking it as such.
+
== The Mod File ==
 
 
==== What is <code>@Mod</code>? ====
 
 
 
This is an annotation indicating to the Forge Mod Loader that the class is a Mod entry point. The <code>@Mod</code> annotation’s value should match a mod id in the <code>src/main/resources/META-INF/mods.toml</code> file.
 
 
 
==== Keeping Your Code Clean Using Sub-packages ====
 
 
 
Rather than clutter up a single class and package with everything, it is recommended you break your mod into subpackages.
 
 
 
A common subpackage strategy has packages for <code>common</code> and <code>client</code> code, which is code that can be run on server/client and client, respectively. Inside the <code>common</code> package would go things like Items, Blocks, and Tile Entities (which can each in turn be another subpackage). Things like GUIs and Renderers would go inside the <code>client</code> package.
 
 
 
{{Colored box|title=Note|content=This package style is only a suggestion, though it is a commonly used style. Feel free to use your own packaging system.}}
 
 
 
By keeping your code in clean subpackages, you can grow your mod much more organically.
 
  
==== Class Naming Schemes ====
+
The main mod class - the class that is annotated with <code>@Mod</code> - is usually put into the main package of your mod, and not placed into a subpackage. This allows an easier time to access this, as your main mod class is the entrypoint of your mod.
  
A common class naming scheme allows easier deciphering of what a class is, and also makes it easier for someone developing with your mod to find things.
+
The <code>@Mod</code> annotation indicates to the mod loader that this class is the entry point of your mod. Each <code>@Mod</code> annotation and their value should be paired with a mod id entry in your <code>mods.toml</code> file.
  
For Example:
+
== mods.toml file ==
  
* An <code>Item</code> called <code>PowerRing</code> would be in an <code>item</code> package, with a class name of <code>PowerRingItem</code>.
+
The <code>mods.toml</code> file is read by the mod loader to determine what mods are packaged into your JAR file, and what information to display to the user in the Mods listing screen (accessible by pressing the "Mods" button on the main menu of the game).  
* A <code>Block</code> called <code>NotDirt</code> would be in a <code>block</code> package, with a class name of <code>NotDirtBlock</code>.
 
* Finally, a <code>TileEntity</code> for a block called <code>SuperChewer</code> would be in a <code>tile</code> or <code>tileentity</code> package, with a class name of <code>SuperChewerTile</code>.
 
  
Appending your class names with what ''kind'' of object they are makes it easier to figure out what a class is, or guess the class for an object.
+
More information about the structure of this file and the possible configuration options available to you can be found on the [[Mods.toml file|dedicated page]].
  
 
[[Category:Getting Started]]
 
[[Category:Getting Started]]
 +
[[Category:Beginner Topics]]

Latest revision as of 21:46, 2 September 2021

The structure of your mod is important in keeping your mod organized, for both your benefit and the benefit of anyone who wishes to make a feature or an add-on for your mod. A disorganized mod structure may cause headaches when someone is trying to update it to a higher version, especially if they cannot modify the package structure, due to i.e. licensing.

Note that this page is only a recommendation for your mod structure; you may structure your mod in any way you see fit.

Packaging

Pick a unique package name for your mod. If you own a URL associated with your project, you can use it as your top level package. For example if you own "example.com", you may use com.example as your top level package. The next level package is usually your mod's ID: if your mod ID is examplemod, your mod's package will be com.example.examplemod.

Important

Do not use a domain for your top level package if you do not own that domain. It is perfectly acceptable to have your top level package be named with anything, such as your name/nickname, or the name of the mod (me.exampledude.examplemod).

Using Subpackages

Rather than clutter up a single class and package with everything, it is recommended you break your mod into subpackages. Below are a few possible methods for laying out your folder structure though you may as well come up with your own layout too.

  • Group by Logic: One possible strategy is to make subpackages for logical units of your mod. For example, if you have a block called Super Furnace you would put its block, its block entity and its item all under feature/superfurnace.
  • Group by Function: Another common strategy is to make subpackages for grouping classes that have a common purpose. For example, your blocks classes can be under blocks, your entities classes can be under entities, your helper utilities can be under helpers, etc.

No matter how your final structure looks like it is highly recommended to add a client subpackage under your main package. This helps to isolate your client-only code from the rest, such as your Screens and renderers.

By keeping your code in clean subpackages, you can grow your mod much more organically.

If you are unsure on how exactly you want to structure your mod it can be a good idea to look at the source of others to see how they did it.

Class Naming Schemes

A common class naming scheme allows easier deciphering of the purpose of the class, and makes it easier for someone developing for your mod to find specific classes.

The usual style is to use suffixes for your classes, for example:

  • An Item called PowerRing would have a class name of PowerRingItem.
  • A Block called NotDirt would have a class name of NotDirtBlock.
  • A BlockEntity for a block called SuperChewer would have a class name of SuperChewerBlockEntity.

The Mod File

The main mod class - the class that is annotated with @Mod - is usually put into the main package of your mod, and not placed into a subpackage. This allows an easier time to access this, as your main mod class is the entrypoint of your mod.

The @Mod annotation indicates to the mod loader that this class is the entry point of your mod. Each @Mod annotation and their value should be paired with a mod id entry in your mods.toml file.

mods.toml file

The mods.toml file is read by the mod loader to determine what mods are packaged into your JAR file, and what information to display to the user in the Mods listing screen (accessible by pressing the "Mods" button on the main menu of the game).

More information about the structure of this file and the possible configuration options available to you can be found on the dedicated page.