Skip to content

[1.20.x] Grammatical fixes and improved readability #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 1.20.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions docs/concepts/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ An event handler is some method that has been registered to an event bus.
Creating an Event Handler
-------------------------

Event handlers methods have a single parameter and do not return a result. The method could be static or instance depending on implementation.
Event handler methods have a single parameter and do not return a result. The method could be static or instance depending on implementation.

Event handlers can be directly registered using `IEventBus#addListener` for or `IEventBus#addGenericListener` for generic events (as denoted by subclassing `GenericEvent<T>`). Either listener adder takes in a consumer representing the method reference. Generic event handlers need to specify the class of the generic as well. Event handlers must be registered within the constructor of the main mod class.

Expand Down Expand Up @@ -98,7 +98,7 @@ If an event can be canceled, it will be marked with the `@Cancelable` annotation
Results
-------

Some events have an `Event$Result`. A result can be one of three things: `DENY` which stops the event, `DEFAULT` which uses the Vanilla behavior, and `ALLOW` which forces the action to take place, regardless if it would have originally. The result of an event can be set by calling `#setResult` with an `Event$Result` on the event. Not all events have results; an event with a result will be annotated with `@HasResult`.
Some events have an `Event$Result`. A result can be one of three things: `DENY` which stops the event, `DEFAULT` which uses the Vanilla behavior, and `ALLOW` which forces the action to take place, regardless of whether it would have taken place originally. The result of an event can be set by calling `#setResult` with an `Event$Result` on the event. Not all events have results; an event with a result will be annotated with `@HasResult`.

!!! important
Different events may use results in different ways, refer to the event's JavaDoc before using the result.
Expand All @@ -111,12 +111,15 @@ Event handler methods (marked with `@SubscribeEvent`) have a priority. You can s
Sub Events
----------

Many events have different variations of themselves. These can be different but all based around one common factor (e.g. `PlayerEvent`) or can be an event that has multiple phases (e.g. `PotionBrewEvent`). Take note that if you listen to the parent event class, you will receive calls to your method for *all* subclasses.
Many events have different variations of themselves. These can be different but all based around one common factor (e.g. `PlayerEvent`) or can be an event that has multiple phases (e.g. `PotionBrewEvent`).

!!! note
Listening to the parent event class will result in getting calls to your method from *all* sub-classes.

Mod Event Bus
-------------

The mod event bus is primarily used for listening to lifecycle events in which mods should initialize. Each event on the mod bus is required to implement `IModBusEvent`. Many of these events are also ran in parallel so mods can be initialized at the same time. This does mean you can't directly execute code from other mods in these events. Use the `InterModComms` system for that.
The mod event bus is primarily used for listening to lifecycle events in which mods should initialize. Each event on the mod bus is required to implement the `IModBusEvent` interface. Many of these events are also ran in parallel in order to allow for faster, parallel mod initialization. **This means you can't directly execute code from other mods in these events.** Use the `InterModComms` system for that.

These are the four most commonly used lifecycle events that are called during mod initialization on the mod event bus:

Expand Down
18 changes: 10 additions & 8 deletions docs/concepts/sides.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ This check should be used as your go-to default. Aside from `DistExecutor`, rare

### `DistExecutor`

Considering the use of a single "universal" jar for client and server mods, and the separation of the physical sides into two jars, an important question comes to mind: How do we use code that is only present on one physical side? All code in `net.minecraft.client` is only present on the physical client. If any class you write references those names in any way, they will crash the game when that respective class is loaded in an environment where those names do not exist. A very common mistake in beginners is to call `Minecraft.getInstance().<doStuff>()` in block or block entity classes, which will crash any physical server as soon as the class is loaded.
Considering the use of a single "universal" jar for client and server mods, and the separation of the physical sides into two jars, an important question comes to mind: How do we use code that is only present on one physical side? All code in `net.minecraft.client` is only present on the physical client. If any class you write references those names in any way, they will crash the game when that respective class is loaded in an environment where those names do not exist. A very common mistake made by beginners is calling `Minecraft.getInstance().<doStuff>()` in block or block entity classes, which will crash any physical server as soon as the class is loaded.

How do we resolve this? Luckily, FML has `DistExecutor`, which provides various methods to run different methods on different physical sides, or a single method only on one side.

!!! note
It is important to understand that FML checks based on the **physical** side. A single player world (logical server + logical client within a physical client) will always use `Dist.CLIENT`!
It is important to understand that FML checks are based on the **physical** side. A single player world (logical server + logical client within a physical client) will always use `Dist.CLIENT`!

`DistExecutor` works by taking in a supplied supplier executing a method, effectively preventing classloading by taking advantage of the [`invokedynamic` JVM instruction][invokedynamic]. The executed method should be static and within a different class. Additionally, if no parameters are present for the static method, a method reference should be used instead of a supplier executing a method.

There are two main methods within `DistExecutor`: `#runWhenOn` and `#callWhenOn`. The methods take in the physical side the executing method should run on and the supplied executing method which either runs or returns a result respectively.
There are two main methods within `DistExecutor`: `#runWhenOn` and `#callWhenOn`. The methods take in the physical side that the executing method should run on and the supplied executing method which either runs or returns a result respectively.

These two methods are subdivided further into `#safe*` and `#unsafe*` variants. Safe and unsafe variants are misnomers for their purposes. The main difference is that when in a development environment, the `#safe*` methods will validate that the supplied executing method is a lambda returning a method reference to another class with an error being thrown otherwise. Within the production environment, `#safe*` and `#unsafe*` are functionally the same.

Expand All @@ -65,7 +65,7 @@ DistExecutor.safeCallWhenOn(Dist.CLIENT, () -> ExampleClass::safeCallMethodExamp

### Thread Groups

If `Thread.currentThread().getThreadGroup() == SidedThreadGroups.SERVER` is true, it is likely the current thread is on the logical server. Otherwise, it is likely on the logical client. This is useful to retrieve the **logical** side when you do not have access to a `Level` object to check `isClientSide`. It *guesses* which logical side you are on by looking at the group of the currently running thread. Because it is a guess, this method should only be used when other options have been exhausted. In nearly every case, you should prefer checking `Level#isClientSide`.
If `Thread.currentThread().getThreadGroup() == SidedThreadGroups.SERVER` is true, it is likely that the current thread is running on the logical server. Otherwise, it is likely running on the logical client. This is useful to retrieve the **logical** side when you do not have access to a `Level` object with which you could check the return value of `isClientSide`. It *guesses* which logical side you are on by looking at the group of the currently running thread. Because it is a guess, this method should only be used when other options have been exhausted. **In nearly every case, you should prefer checking the return value of `Level#isClientSide`**.

### `FMLEnvironment#dist` and `@OnlyIn`

Expand All @@ -88,22 +88,24 @@ This mistake can also be made explicitly by accessing physical client-only class
Writing One-Sided Mods
----------------------

In recent versions, Minecraft Forge has removed a "sidedness" attribute from the mods.toml. This means that your mods are expected to work whether they are loaded on the physical client or the physical server. So for one-sided mods, you would typically register your event handlers inside a `DistExecutor#safeRunWhenOn` or `DistExecutor#unsafeRunWhenOn` instead of directly calling the relevant registration methods in your mod constructor. Basically, if your mod is loaded on the wrong side, it should simply do nothing, listen to no events, and so on. A one-sided mod by nature should not register blocks, items, ... since they would need to be available on the other side, too.
In recent versions, Minecraft Forge has removed a "sidedness" attribute from the mods.toml. **This means that your mods are expected to work whether they are loaded on the physical client or the physical server.** So for one-sided mods, you would typically register your event handlers inside a `DistExecutor#safeRunWhenOn` or `DistExecutor#unsafeRunWhenOn` instead of directly calling the relevant registration methods in your mod constructor. Basically, if your mod is loaded on the wrong side, it should simply do nothing, listen to no events, and so on. A one-sided mod by nature should not register blocks, items, etc. since they would need to be available on the other side, too.

Additionally, if your mod is one-sided, it typically does not forbid the user from joining a server that is lacking that mod. Therefore, you should set the `displayTest` property in your [mods.toml][structuring] to whatever value is necessary.

```toml
[[mods]]
# ...

# MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod.
# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod.
# MATCH_VERSION means that your mod will cause a red X if the versions on client and server mismatch. This is the default behaviour and should be what you choose if you have server and client elements to your mod.
# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if your mod is a server only mod.
# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component.
# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value.
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) wherever it finds itself.
displayTest="IGNORE_ALL_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional)
```

!!! note
This is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) wherever it finds itself.

If a custom display test is to be used, then the `displayTest` option should be set to `NONE`, and an `IExtensionPoint$DisplayTest` extension should be registered:

```java
Expand Down
15 changes: 10 additions & 5 deletions docs/gettingstarted/modfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ showAsResourcePack=false
side="BOTH"
```

`mods.toml` is broken into three parts: the non-mod-specific properties, which are linked to the mod file; the mod properties, with a section for each mod; and the dependency configurations, with a section for each mod's or mods' dependencies. Each of the properties associated with the `mods.toml` file will be explained below, where `required` means that a value must be specified or an exception will be thrown.
`mods.toml` is broken into three parts:
* the non-mod-specific properties, which are linked to the mod file
* the mod properties, with a section for each mod
* the dependency configurations, with a section for each mod's (or mods') dependencies.

Each of the properties associated with the `mods.toml` file will be explained below, where `required` means that a value must be specified or an exception will be thrown.

### Non-Mod-Specific Properties

Expand Down Expand Up @@ -82,8 +87,8 @@ modId = "examplemod2"

Property | Type | Default | Description | Example
:--- | :---: | :---: | :---: | :---
`modId` | string | **mandatory** | The unique identifier representing this mod. The id must match `^[a-z][a-z0-9_]{1,63}$` (a string 2-64 characters; starts with a lowercase letter; made up of lowercase letters, numbers, or underscores). | `"examplemod"`
`namespace` | string | value of `modId` | An override namespace for the mod. The namespace much match `^[a-z][a-z0-9_.-]{1,63}$` (a string 2-64 characters; starts with a lowercase letter; made up of lowercase letters, numbers, underscores, dots, or dashes). Currently unused. | `"example"`
`modId` | string | **mandatory** | The unique identifier representing this mod. The id must match the following regular expression: `^[a-z][a-z0-9_]{1,63}$` (a string that is 2-64 characters long; starts with a lowercase letter; contains only lowercase letters, numbers, or underscores). | `"examplemod"`
`namespace` | string | value of `modId` | An override namespace for the mod. The namespace must match the following regular expression: `^[a-z][a-z0-9_.-]{1,63}$` (a string that is 2-64 characters long; starts with a lowercase letter; contains only lowercase letters, numbers, underscores, dots, or dashes). Currently unused. | `"example"`
`version` | string | `"1"` | The version of the mod, preferably in a [variation of Maven versioning][mvnver]. When set to `${file.jarVersion}`, it will be replaced with the value of the `Implementation-Version` property in the JAR's manifest (displays as `0.0NONE` in a development environment). | `"1.20-1.0.0.0"`
`displayName` | string | value of `modId` | The pretty name of the mod. Used when representing the mod on a screen (e.g., mod list, mod mismatch). | `"Example Mod"`
`description` | string | `"MISSING DESCRIPTION"` | The description of the mod shown in the mod list screen. It is recommended to use a [multiline literal string][multiline]. | `"This is an example."`
Expand Down Expand Up @@ -125,11 +130,11 @@ Property | Type | Default | Description | Example
Mod Entrypoints
---------------

Now that the `mods.toml` is filled out, we need to provide an entrypoint to being programming the mod. Entrypoints are essentially the starting point for executing the mod. The entrypoint itself is determined by the language loader used in the `mods.toml`.
Now that `mods.toml` is filled out, we need to define an entry point to start programming the mod. An entry point is essentially the starting point of your mod, this is where your mod's code begins execution. The entry point itself is determined by the language loader specified in `mods.toml`.

### `javafml` and `@Mod`

`javafml` is a language loader provided by Forge for the Java programming language. The entrypoint is defined using a public class with the `@Mod` annotation. The value of `@Mod` must contain one of the mod ids specified within the `mods.toml`. From there, all initialization logic (e.g., [registering events][events], [adding `DeferredRegister`s][registration]) can be specified within the constructor of the class. The mod bus can be obtained from `FMLJavaModLoadingContext`.
`javafml` is a language loader provided by Forge for the Java programming language. The entry point is defined as a public class, that uses the `@Mod` annotation. The value of `@Mod` must contain one of the mod ids specified within `mods.toml`. From there, all initialization logic (e.g., [registering events][events], [adding `DeferredRegister`s][registration]) can be specified within the constructor of the class. The mod event bus can be obtained from `FMLJavaModLoadingContext`.

```java
@Mod("examplemod") // Must match mods.toml
Expand Down
2 changes: 1 addition & 1 deletion docs/gettingstarted/structuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ You can find some additional naming conventions on [Oracle's tutorial page][nami

### Sub-package Organization

In addition to the top-level package, it is highly recommend to break your mod's classes between subpackages. There are two major methods on how to do so:
In addition to the top-level package, it is highly recommended to break your mod's classes into subpackages. There are two major methods for doing so:

* **Group By Function**: Make subpackages for classes with a common purpose. For example, blocks can be under `block` or `blocks`, entities under `entity` or `entities`, etc. Mojang uses this structure with the singular version of the word.
* **Group By Logic**: Make subpackages for classes with a common logic. For example, if you were creating a new type of crafting table, you would put its block, menu, item, and more under `feature.crafting_table`.
Expand Down