Skip to content

Commit

Permalink
Merge better-mi-integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Swedz committed Aug 28, 2024
2 parents e2c2a52 + e39b6f4 commit c52333f
Show file tree
Hide file tree
Showing 86 changed files with 2,125 additions and 602 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ Tesseract is another library mod for mod developers to simplify their developmen

## Main Features

- Hook framework and API for making addons for [Modern Industrialization](https://modrinth.com/mod/modern-industrialization)
- Hook framework and API for making addons
for [Modern Industrialization](https://modrinth.com/mod/modern-industrialization)
- Block, Item, and Fluid registering helpers
- Some extra events

## Documentation

For any information regarding how to use Tesseract, see the documentation [here](https://github.com/Swedz/tesseract-neoforge/tree/master/docs). If you have any questions regarding Tesseract, feel free to ask in the `#coding-stuff` channel on [my discord](https://discord.gg/vNaqDzSNaB).
For any information regarding how to use Tesseract, see the
documentation [here](https://github.com/Swedz/tesseract-neoforge/tree/master/docs). If you have any questions regarding
Tesseract, feel free to ask in the `#coding-stuff` channel on [my discord](https://discord.gg/vNaqDzSNaB).
17 changes: 10 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ base {
}

repositories {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
mavenLocal()
maven {
name = "Modmaven"
url = "https://modmaven.dev"
content {
// GrandPower
includeGroup "dev.technici4n"
// MI
// Modern Industrialization
includeGroup "aztech"
}
}
Expand Down Expand Up @@ -111,7 +108,8 @@ if (System.getenv("CURSEFORGE_API_KEY")) {
changelogType = "markdown"
delegate.changelog = System.getenv("MOD_CHANGELOG")

addGameVersion project.minecraft_version
def compatible_minecraft_versions = project.compatible_minecraft_versions.split(",")
compatible_minecraft_versions.each { addGameVersion it }
addGameVersion "NeoForge"
addGameVersion "Java 21"

Expand All @@ -138,7 +136,7 @@ if (System.getenv("MODRINTH_API_KEY")) {
versionType = project.release_type
delegate.changelog = System.getenv("MOD_CHANGELOG")

gameVersions = [project.minecraft_version]
gameVersions = project.compatible_minecraft_versions.split(",")
loaders = ["neoforge"]

dependencies {
Expand All @@ -163,6 +161,11 @@ neoForge {
"src/main/resources/${mod_id}.accesstransformer.cfg"
]

parchment {
mappingsVersion = project.parchment_mappings_version
minecraftVersion = project.minecraft_version
}

runs {
configureEach {
mods = [neoForge.mods."${mod_id}"]
Expand Down
76 changes: 58 additions & 18 deletions docs/MI_HOOKS.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,90 @@
# Modern Industrialization Hooks
Normally making addons for MI is nontrivial, but this system aims to lower the barrier of entry significantly for those who may want to make them.

Normally making addons for MI is nontrivial, but this system aims to lower the barrier of entry significantly for those
who may want to make them.

## Registering a Hook
First you need to register your mod with the hook system. To do so, you must provide a hook registry and a hook listener.

To register your mod's hook registry and listener, simply include the `@TesseractMIHookEntrypoint` annotation on your classes. Be sure to include it on both the registry and the listener.
First you need to register your mod with the hook system. To do so, you must provide a hook registry and a hook
listener.

To register your mod's hook registry and listener, simply include the `@MIHookEntrypoint` annotation on your classes. Be
sure to include it on both the registry and the listener.

### Hook Registries
Hook registries are defined by creating an implementation of the `MIHookRegistry` interface. This is used by the hook system to direct blocks, block entities, items, and recipe types to your own defined registries.

Although it is not required to, it is recommended to take the registered objects provided in the `on____Register` methods and save them to a collection of some kind in your codebase. These methods will be called whenever something has been registered by your listener.
Hook registries are defined by creating an implementation of the `MIHookRegistry` interface. This is used by the hook
system to direct blocks, block entities, items, and recipe types to your own defined registries.

Although it is not required to, it is recommended to take the registered objects provided in the `on____Register`
methods and save them to a collection of some kind in your codebase. These methods will be called whenever something has
been registered by your listener.

Note that blocks and items registered through this system will be created and provided to you in `BlockHolder`s and `ItemHolder`s respectively. These are registerable object wrappers that are futher explained in [REGISTRY_HELPERS.md](REGISTRY_HOLDERS.md). If you have your own system you would like to use for things like this, feel free to convert the objects provided to you to your own versions.
Note that blocks and items registered through this system will be created and provided to you in `BlockHolder`s
and `ItemHolder`s respectively. These are registerable object wrappers that are futher explained
in [REGISTRY_HELPERS.md](REGISTRY_HOLDERS.md). If you have your own system you would like to use for things like this,
feel free to convert the objects provided to you to your own versions.

If you don't use a registry in your listener, it is "safe" to return `null` or throw an exception. For example, it is not required to create a block registry for this if you do not register any blocks (this includes machines). Additionally, if you do not use any of the registries in your listener, you can use `MIHookRegistry.NONE` as your registry to simply return `null` for all registries.
If you don't use a registry in your listener, it is "safe" to return `null` or throw an exception. For example, it is
not required to create a block registry for this if you do not register any blocks (this includes machines).
Additionally, if you do not use any of the registries in your listener, you can use `MIHookRegistry.NONE` as your
registry to simply return `null` for all registries.

### Hook Listeners
Hook listeners are defined by creating an implementation of the `MIHookListener` interface. None of the methods require implementation, so you only need to implement the methods you need for your hook. These methods will be called by the hook system to request any additions.

The method names are rather intuitive, and they provide hook contexts to make registering various machines, recipe types, or many other MI-related features rather trivial.
Hook listeners are defined by creating an implementation of the `MIHookListener` interface. None of the methods require
implementation, so you only need to implement the methods you need for your hook. These methods will be called by the
hook system to request any additions.

The method names are rather intuitive, and they provide hook contexts to make registering various machines, recipe
types, or many other MI-related features rather trivial.

### Efficiency Hooks
Efficiency hooks are defined by creating an implementation of the `MIHookEfficiency` interface and giving it the `@TesseractMIHookEntrypoint` annotation (just like how you do with your hook registry and listener). None of the methods require implementation, so you only need to implement the methods you need for your hook. These methods will be called by the hook system to request any changes to the efficiency values at each point.

Efficiency hooks are called in order of priority (highest first, lowest last). If `shouldAlwaysRun()` returns `false` (default), the hook will only be called if no modification to the context has been made by any higher priority hooks. It is not recommended to touch this unless you know what you're doing.
Efficiency hooks are defined by creating an implementation of the `MIHookEfficiency` interface and giving it
the `@MIHookEntrypoint` annotation (just like how you do with your hook registry and listener). None of the methods
require implementation, so you only need to implement the methods you need for your hook. These methods will be called
by the hook system to request any changes to the efficiency values at each point.

Efficiency hooks are called in order of priority (highest first, lowest last). If `shouldAlwaysRun()` returns `false` (
default), the hook will only be called if no modification to the context has been made by any higher priority hooks. It
is not recommended to touch this unless you know what you're doing.

The different `on____` methods in the hook are called at different injection points of the crafter component and crafter behavior code. This is also compatible with the modular crafter component framework provided by Tesseract. For each of these contexts, only some variables are modifiable. See the javadocs for each method to see what variables are allowed to be modified.
The different `on____` methods in the hook are called at different injection points of the crafter component and crafter
behavior code. This is also compatible with the modular crafter component framework provided by Tesseract. For each of
these contexts, only some variables are modifiable. See the javadocs for each method to see what variables are allowed
to be modified.

### Working Example
For a working example of a registry and listener, see [my implementation in Extended Industrialization](https://github.com/Swedz/Extended-Industrialization/tree/master/src/main/java/net/swedz/extended_industrialization/compat/mi).

For a working example of a registry and listener,
see [my implementation in Extended Industrialization](https://github.com/Swedz/Extended-Industrialization/tree/master/src/main/java/net/swedz/extended_industrialization/compat/mi).

## Hook Asset Datagen
It is strongly recommended to use datagen for creating assets for things registered by your hook listener. For cases that are not listed below, see [my implementation in Extended Industrialization](https://github.com/Swedz/Extended-Industrialization/tree/master/src/main/java/net/swedz/extended_industrialization/datagen).

It is strongly recommended to use datagen for creating assets for things registered by your hook listener. For cases
that are not listed below,
see [my implementation in Extended Industrialization](https://github.com/Swedz/Extended-Industrialization/tree/master/src/main/java/net/swedz/extended_industrialization/datagen).

### Registering Finicky-ness
There are some cases where some assets must still get registered under MI's namespace. Aside from minor annoyance, this isn't really that much of an issue.

There are some cases where some assets must still get registered under MI's namespace. Aside from minor annoyance, this
isn't really that much of an issue.

#### Language
If you create any machine recipe types in your hook listener, you may notice that the english name you provided is not displaying in your recipe indexer (JEI/REI/EMI). This can be resolved by adding the following line of code to your `GatherDataEvent` listener and running your datagen.

If you create any machine recipe types in your hook listener, you may notice that the english name you provided is not
displaying in your recipe indexer (JEI/REI/EMI). This can be resolved by adding the following line of code to your
implementation of `LanguageProvider` and running your datagen.

```java
MIDatagenHooks.Client.addLanguageHook(event, "<your mod id>");
MIDatagenHooks.Client.withLanguageHook(this, "<your mod id>");
```

#### Machine Casing Models
If you create any machine casings in your hook listener, you may notice that the texture appears broken. This can be resolved by adding the following line of code to your `GatherDataEvent` listener and running your datagen.

If you create any machine casings in your hook listener, you may notice that the texture appears broken. This can be
resolved by adding the following line of code to your `GatherDataEvent` listener and running your datagen.

```java
MIDatagenHooks.Client.addMachineCasingModelsHook(event, "<your mod id>");
Expand Down
58 changes: 58 additions & 0 deletions docs/PROXIES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Proxies

Sometimes when making mods you need to access client-only code in an ambiguous environment (it may be client or may be
server). This can get very dangerous if not handled properly, and the simplest solution I've seen is with some kind of
proxy. Tesseract provides its own take on the proxy solution using annotations to register proxies with different
environments.

Below is an overview of each proxy environment and how to use them.

## Creating a Proxy

Making a proxy is relatively straightforward. For a proxy to make any sense, you need at minimum two classes. The first
class will act as your "common" proxy which will always get loaded by default, and then the second class will act as
your "conditional" proxy which will only get loaded in certain environments. It is possible to have more than one
conditional proxy, and a priority can be defined in the annotation. Proxies with a higher priority will override proxies
with lower priorities.

Proxies are loaded and initialized on startup automatically by the Tesseract mod itself. Proxies will not be loaded
at mixin time, but will be loaded once Tesseract is loaded.

Here's an example common proxy that would always get loaded by default. If there is a conditional proxy that extends
this proxy and has its condition met, it will override this one.

```java
@ProxyEntrypoint
public class MyProxy implements Proxy {}
```

Here's an example conditional proxy that only would get loaded on the client side.

```java
@ProxyEntrypoint(environment = ProxyEnvironment.CLIENT)
public class MyClientProxy extends MyProxy {}
```

There's also the option to make a conditional proxy that is only loaded if a certain mod is loaded. Here's an example of
how that would be done:

```java
@ProxyEntrypoint(environment = ProxyEnvironment.MOD, modid = "<mod id>")
public class MyModProxy extends MyProxy {}
```

A proxy entrypoint can also use multiple environments, so if you wanted a proxy that would only be loaded when a mod is
loaded *and* the environment is the client, that can be done too. By using the priority system, you can have multiple
different conditional proxies that are loaded with different conditions.

The `Proxy` interface has one empty method, `init()`, that is called when the proxy is initialized. This method can be
overridden to run code immediately when that proxy is loaded on startup.

## Using a Proxy

Using a proxy is pretty straight forward. Proxies are referenced to by their super-most class other than the `Proxy`
interface itself. Here's an example of how you can grab your proxy and use it:

```java
ProxyManager.get(MyProxy.class).yourMethodHere();
```
20 changes: 16 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Documentation
This is the documentation for Tesseract's NeoForge Module. For any questions, please ask in the `#coding-stuff` channel on [my discord](https://discord.gg/vNaqDzSNaB).

This is the documentation for Tesseract's NeoForge Module. For any questions, please ask in the `#coding-stuff` channel
on [my discord](https://discord.gg/vNaqDzSNaB).

## Hooking into Modern Industrialization
Although I am not a dev for [Modern Industrialization](https://github.com/AztechMC/Modern-Industrialization), Tesseract provides the ability for mods to hook into it rather easily while also avoiding conflicts with other mods.

Although I am not a dev for [Modern Industrialization](https://github.com/AztechMC/Modern-Industrialization), Tesseract
provides the ability for mods to hook into it rather easily while also avoiding conflicts with other mods.

Refer to [MI_HOOKS.md](MI_HOOKS.md) for further information.

## Registry Holders
There are some relatively basic registry holders provided by Tesseract that are useful for quickly and simply registering blocks, items, and fluids. The MI Hook system uses the registry holders internally.

Refer to [REGISTRY_HOLDERS.md](REGISTRY_HOLDERS.md) for further information.
There are some relatively basic registry holders provided by Tesseract that are useful for quickly and simply
registering blocks, items, and fluids. The MI Hook system uses the registry holders internally.

Refer to [REGISTRY_HOLDERS.md](REGISTRY_HOLDERS.md) for further information.

## Proxies

Tesseract provides a very simple and quick way to implement proxies that have different load environments.

Refer to [PROXIES.md](PROXIES.md) for further information.
31 changes: 24 additions & 7 deletions docs/REGISTRY_HOLDERS.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
# Registry Holders
Provided in Tesseract are what are defined as `RegisteredObjectHolder`s, or more simply put: "registry holders". These holders "hold" a registerable object type and provide helper methods for defining how they should be registered and used.

Provided in Tesseract are what are defined as `RegisteredObjectHolder`s, or more simply put: "registry holders". These
holders "hold" a registerable object type and provide helper methods for defining how they should be registered and
used.

Below is a brief overview of each type of holder and a simple example.

## Items
`ItemHolder<T>`s are used to define items. The `<T>` generic in this case defines the `Item` type for the item. In most cases, this is just `Item`.

`ItemHolder<T>`s are used to define items. The `<T>` generic in this case defines the `Item` type for the item. In most
cases, this is just `Item`.

## Blocks
`BlockHolder<T>`s are used to define blocks. The `<T>` generic in this case defines the `Block` type for the block. In most cases, this is just `Block`.

`BlockHolder<T>`s are used to define blocks. The `<T>` generic in this case defines the `Block` type for the block. In
most cases, this is just `Block`.

### Blocks with Items
`BlockWithItemHolder<B, I>`s are used to define blocks that also have an item. The `<B>` generic in this case defines the `Block` type for the block, and the `<I>` generic defines the `Item` type for the item. In most cases these are just `Block` and `BlockItem`.

`BlockWithItemHolder<B, I>`s are used to define blocks that also have an item. The `<B>` generic in this case defines
the `Block` type for the block, and the `<I>` generic defines the `Item` type for the item. In most cases these are
just `Block` and `BlockItem`.

Most blocks should be defined as a block with item, as the item part refers to the item used in inventories.

## Fluids

`FluidHolder<F, FT, FB, FBI>`s are used to define fluids. The generics are as follows:

- `<F>`: `Fluid`
- `<FT>`: `FluidType`
- `<FB>`: `Block` representing the fluid in world
- `<FBI>`: `BucketItem` to use for the bucket

Fluid holders are a bit more complex than the other holders. It is recommended to make a fluid holder implementation of your own for your fluids.
Fluid holders are a bit more complex than the other holders. It is recommended to make a fluid holder implementation of
your own for your fluids.

### Modern Industrialization-like Fluids
If you are making an addon for MI, you can use the `MIFluidHolder` fluid holder implementation. This implementation streamlines the process of making fluids by using MI's fluid api.

If you are making an addon for MI, you can use the `MIFluidHolder` fluid holder implementation. This implementation
streamlines the process of making fluids by using MI's fluid api.

### Fluid Asset Datagen
It is strongly recommended to use the builtin datagen providers for generating fluid textures and models (including buckets). Note that this should work properly for all kinds of `FluidHolder`s, but requires MI to function.

It is strongly recommended to use the builtin datagen providers for generating fluid textures and models (including
buckets). Note that this should work properly for all kinds of `FluidHolder`s, but requires MI to function.

The below code is an example of what you can place inside of your `GatherDataEvent` listener.

Expand Down
Loading

0 comments on commit c52333f

Please sign in to comment.