Skip to content
Merged
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
8 changes: 0 additions & 8 deletions docs/content/Development/External-Resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ as other resources you might find helpful.
- [MCJty's Modding Wiki](https://www.mcjty.eu/docs/intro)


## LDLib

LDLib is the main library we're using for GTCEu-Modern.

!!! link "LDLib Docs"
[:material-github: LDLib-Architectury :material-arrow-right: Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki)


## Mixins

!!! link "Overview on using Mixins"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ title: Item/Fluid/Energy Storage

!!! note

In general, these containers should be created as `final` fields (that's what we need for the
[SyncData](../SyncData/index.md) system).
In general, these containers should be created as `final` fields.
Set their base arguments in the constructor (you can pass args for subclasses to modify).


Expand Down Expand Up @@ -47,7 +46,6 @@ To do so, use either of the following interfaces:
- `IFluidTransfer`
- `IEnergyContainer`


## Specialized proxy implementations

In case you have special requirements to your containers, you may be able to use one of these implementations in
Expand All @@ -61,67 +59,9 @@ They generally act as a proxy to the underlying container(s), while also handlin
- `FluidTransferList`
- `EnergyContainerList`


### IO-specific container proxies

For proxying multiple containers, limited to a specific IO direction.

- `IOItemTransferList`
- `IOFluidTransferList`


### Rate-Limited proxies

!!! warning inline end "Not merged yet<br>_Branch: `mi-ender-link`_"

If you need to proxy any item or fluid container that needs to be rate limited for insertion and extraction, you can
use either of the following classes:

- `LimitingItemTransferProxy`
- `LimitingFluidTransferProxy`

The transfer limit passed as a constructor parameter will not renew automatically. Your container will therefore stop
transferring anything once this limit is reached.

If you want to make this a rate limit instead, you will have to schedule a task that regularly resets the transfer
limit to the maximum value for your task's interval:

??? example "Example Usage"

```java
public class MyCover extends CoverBehavior {
private LimitingFluidTransferProxy transferProxy;
private ConditionalSubscriptionHandler rateLimitSubscription;

public MyCover(IFluidTransfer myFluidTransfer) {
super(/* ... */);

transferProxy = new LimitingFluidTransferProxy(
myFluidTransfer,
0L // Initial limit of 0, will be updated regularly in isRateLimitRefreshActive()
);
rateLimitSubscription = new ConditionalSubscriptionHandler(
this,
this::resetTransferRateLimit,
this::isRateLimitRefreshActive
);
}

@Override
public void onLoad() {
super.onLoad();
rateLimitSubscription.initialize(coverHolder.getLevel());
}

private void resetTransferRateLimit() {
if (transferProxy == null)
return;

transferProxy.setRemainingTransfer(transferRate.getMilliBuckets() * 20);
}

private boolean isRateLimitRefreshActive() {
// ...
}
}
```
30 changes: 0 additions & 30 deletions docs/content/Development/General-Topics/Global-Data.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,3 @@ In certain cases (e.g. in a cache that holds all currently loaded instances of a
in a global (static and mutable) variable.

When doing so, you need to ensure that remote and serverside instances don't get mixed up.


## Using `SideLocal<T>`

!!! warning inline end "Not yet merged<br>_Branch: `mi-ender-link`_"

To make working with this requirement easier, You can use `SideLocal<T>` to store your global data.
It is similar to Java's `ThreadLocal`, but operates on the game's sides instead.

If you are currently on the remote side (`GTCEuAPI.isClientThread()` / on the client's `main` thread), it will return the
remote side's instance of your data. Otherwise, you will get the server side's instance.

??? example "Example Usage"

```java
public class MyCache {
private static SideLocal<Map<UUID, MyData>> cache = new SideLocal<>(HashMap::new);

public static void cacheData(UUID id, MyData data) {
cache.get().put(id, data);
}

public static MyData getData(UUID id) {
return cache.get().get(id);
}
}
```

Alternatively to passing an initializer for both instances to `SideLocal`'s constructor, you can also supply
separate instances for the remote and server side.
107 changes: 107 additions & 0 deletions docs/content/Development/General-Topics/Machine-Trait-System.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
title: Machine Trait System
---

# Machine Trait System

The machine trait system allows for attaching extra capabilities and behaviours to a machine by attaching traits to machine instances. Traits can listen for specific machine events, interactions, and provide capabilities, allowing for machine functionality to be implemented via a composition-based design.

## Attaching and using machine traits

Attaching traits must be done before machine instances are fully initialised, and are typically attached in the constructor.

### Attaching and using traits:

Attaching a machine trait to a machine is done with the `MetaMachine::attachTrait` and `MetaMachine::attachPersistentTrait` methods.

When attaching traits, a `callbackPriority` value can be given. Traits with a higher priority will have their events and interactions called
first, which may prevent traits with a lower priority from handling some events.

!!! warning
Traits must be attached to a machine instance via either `attachTrait` or `attachPersistentTrait`.
If a trait is created without either of these methods being called, the trait will not be valid.

```java
public class CustomMachine extends MetaMachine {

@SaveField
protected final NotifiableFluidTank tank;

public CustomMachine(BlockEntityCreationInfo info, int capacity) {
super(info);

// Because the tank field is annotated with `@SaveField`, the fluid tank will be saved into the machines data.
this.tank = attachTrait(new NotifiableFluidTank(1, capacity, IO.BOTH));

// Registers an auto output trait that provides fluid output behaviour for the given fluid tank.
// Instead of using an annotated field to save traits, they can also be registered to be saved.
// The trait save name should remain the same, otherwise the trait save data won't be loaded.
AutoOutputTrait autoOutput = attachPersistentTrait("autoOutput", AutoOutputTrait.ofFluids(tank));

autoOutput.setFluidOutputDirection(Direction.DOWN);
autoOutput.setFluidOutputDirectionValidator(d -> d == Direction.DOWN);
}

public void usingTraits() {
MetaMachine machine = getMachine();

// Most trait objects have a `TYPE` static field, it can be used to get traits with a specific type.
AutoOutputTrait autoOutputTrait = machine.getTrait(AutoOutputTrait.TYPE);
Optional<RecipeLogic> recipeLogicOptional = machine.getTrait(RecipeLogic.TYPE);

// Gets all traits with the specified type.
List<NotifiableItemStackHandler> allItemStackHandlers = machine.getTraits(NotifiableItemStackHandler.TYPE);

List<MachineTrait> allTraits = machine.getAllTraits();
}
}
```

### Creating custom traits

Custom machine traits are created by extending `MachineTrait` or `MultiblockMachineTrait`

Machine traits have access to a number of machine events and callbacks, but some extra behaviours can be added by having a trait implement a trait feature interface. For example, `IInteractionTrait` to add custom interaction behaviour. The full list of trait features is in `api/machine/trait/feature`.

```java
public class CustomMachineTrait extends MachineTrait implements IInteractionTrait {

// Machine traits should have a type object defined, unless a parent class of this machine trait already defines a type
public static final MachineTraitType<CustomMachineTrait> TYPE = new MachineTraitType<>(CustomMachineTrait.class);

public CustomMachineTrait() {

}

// Machine traits must also define a getter for the trait type
@Override
public MachineTraitType<CustomMachineTrait> getType() {
return TYPE;
}

// A list of classes or interfaces which a machine must be in order for this trait to be attached.
// A machine trait must be at least one of these interfaces/classes.
// By default, traits can be attached to any machine.
@Override
protected List<Class<?>> validMachineClasses() {
return List.of(CustomMachine.class);
}

// An example of a machine trait event callback.
// Traits with a higher trait priority will have their events called first,
// which may block lower priority traits from receiving events.
// All traits default to a priority of 1.
@Override
public Pair<@Nullable GTToolType, InteractionResult> onToolClick(ExtendedUseOnContext context) {
var toolType = context.getToolType();
if (toolType.contains(GTToolType.WRENCH)) {
return Pair.of(GTToolType.WRENCH, onWrenchClick(context));
}
return IInteractionTrait.super.onToolClick(context);
}

private InteractionResult onWrenchClick(ExtendedUseOnContext context) {
return InteractionResult.PASS;
}
}
```
6 changes: 0 additions & 6 deletions docs/content/Development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ pull request with your changes.

The following pages describe a few important concepts that you will likely run into when working with our codebase.

!!! link "LDLib Docs"
[:material-github: LDLib-Architectury :material-arrow-right: Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki)

This mod is based on the LDLib library for a lot of comminly used functionalities.
Please refer to its documentation as well.

!!! link "Architectury Gradle Plugin"
[Architectury-Wiki :material-arrow-right: Gradle Plugin](https://docs.architectury.dev/plugin/introduction)

Expand Down
42 changes: 2 additions & 40 deletions docs/content/Modpacks/Changes/v8.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Machine interactions have been split into two methods: `MetaMachine::onUse` and

A new system for attaching traits and custom behaviour to machines has been added, and many machine interfaces and traits now use this new system.

See [this page](../../Development/General-Topics/Machine-Trait-System.md) for an overview of the new system.

### Machine constructor and trait initialisation changes

The constructors for a large number of machines have changed in order to simply machine instance creation. Additionally, methods for trait creation have also been removed and now form part of the machine constructor.
Expand All @@ -90,8 +92,6 @@ The constructors for a large number of machines have changed in order to simply
- Removed `createRecipeLogic` method
- Constructor now has an optional `Supplier<RecipeLogic>` argument, defaults to the standard `RecipeLogic` class

### New System Example

#### IMPORTANT MIGRATION NOTE:

Traits must be attached to a machine directly by calling `attachTrait(MachineTrait trait)` on the trait being attached.
Expand All @@ -105,44 +105,6 @@ this.itemHandler = attachTrait(new NotifiableItemStackHandler(0, IO.BOTH))
```
When migrating, remove the `this` argument from the machine trait constructor and instead attach the trait.

Example of the new `AutoOutputTrait`

```java
public class CustomDrumMachine extends MetaMachine {

protected final NotifiableFluidTank tank;
public final AutoOutputTrait autoOutput;

public DrumMachine(BlockEntityCreationInfo info, int capacity) {
super(info);

// Traits must be attached in the constructor.

this.tank = attachTrait(new NotifiableFluidTank(1, capacity, IO.BOTH));

// Registers an auto output trait that provides fluid output behaviour for the given fluid tank.
this.autoOutput = attachTrait(AutoOutputTrait.ofFluids(tank));

autoOutput.setFluidOutputDirection(Direction.DOWN);
autoOutput.setFluidOutputDirectionValidator(d -> d == Direction.DOWN);
}

// Any code can query traits from a machine
public static void queryTraits(MetaMachine machine) {

// Returns a trait with the given type, or null.
AutoOutputTrait autoOutputTrait = machine.getTraitHolder().getTrait(AutoOutputTrait.TYPE);

Optional<RecipeLogic> recipeLogicOptional = machine.getTraitHolder().getTrait(RecipeLogic.TYPE);

// Gets all traits with the specified type.
List<NotifiableItemStackHandler> allItemStackHandlers = machine.getTraitHolder().getTraits(NotifiableItemStackHandler.TYPE);

}
}
```


### Removed interfaces

A large number of machine feature interfaces have been removed, and have had their functionality merged into the standard MetaMachine class, or now use the new machine trait system:
Expand Down
1 change: 0 additions & 1 deletion src/generated/resources/assets/gtceu/lang/en_ud.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"behavior.portable_scanner.debug_lag_count": "˙ɹǝʌɹǝS ǝɥʇ uo )sɯ%s uɐɥʇ ɹǝbuoן buıʞɐʇ buıɥʇʎuɐ( sbuıuɹɐM ǝʞıdS bɐꞀ %s pǝsnɐƆ",
"behavior.portable_scanner.debug_machine": "%s :ᗡI-ɐʇǝW",
"behavior.portable_scanner.debug_machine_invalid": "¡pıןɐʌuı ",
"behavior.portable_scanner.debug_machine_invalid_null=invalid! MetaTileEntity =": "¡ןןnu ",
"behavior.portable_scanner.debug_machine_valid": "pıןɐʌ ",
"behavior.portable_scanner.divider": "=========================",
"behavior.portable_scanner.energy_container_in": "Ɐ %s ʇɐ ∩Ǝ )%s( %s :NI xɐW",
Expand Down
1 change: 0 additions & 1 deletion src/generated/resources/assets/gtceu/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"behavior.portable_scanner.debug_lag_count": "Caused %s Lag Spike Warnings (anything taking longer than %sms) on the Server.",
"behavior.portable_scanner.debug_machine": "Meta-ID: %s",
"behavior.portable_scanner.debug_machine_invalid": " invalid!",
"behavior.portable_scanner.debug_machine_invalid_null=invalid! MetaTileEntity =": " null!",
"behavior.portable_scanner.debug_machine_valid": " valid",
"behavior.portable_scanner.divider": "=========================",
"behavior.portable_scanner.energy_container_in": "Max IN: %s (%s) EU at %s A",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player
blockPos.move(Direction.UP);
continue;
}
BlockEntity te = level.getBlockEntity(blockPos);
if (te instanceof PipeBlockEntity<?, ?> pbe && !pbe.getFrameMaterial().isNull()) {
BlockEntity be = level.getBlockEntity(blockPos);
if (be instanceof PipeBlockEntity<?, ?> pbe && !pbe.getFrameMaterial().isNull()) {
blockPos.move(Direction.UP);
continue;
}
Expand All @@ -197,7 +197,7 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player
if (!player.isCreative())
stack.shrink(1);
return InteractionResult.SUCCESS;
} else if (te instanceof PipeBlockEntity<?, ?> pbe && pbe.getFrameMaterial().isNull()) {
} else if (be instanceof PipeBlockEntity<?, ?> pbe && pbe.getFrameMaterial().isNull()) {
pbe.setFrameMaterial(frameBlock.material);

if (!player.isCreative())
Expand All @@ -223,8 +223,8 @@ public static MaterialBlock getFrameboxFromItem(ItemStack stack) {
}

public boolean removeFrame(Level level, BlockPos pos, Player player, ItemStack stack) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof PipeBlockEntity<?, ?> pipeTile) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof PipeBlockEntity<?, ?> pipeTile) {
Material mat = pipeTile.getFrameMaterial();
if (!mat.isNull()) {
pipeTile.setFrameMaterial(GTMaterials.NULL);
Expand Down
Loading
Loading