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
53 changes: 42 additions & 11 deletions docs/content/Development/Data-Sync-System/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,59 @@ title: "Usage"

### Registering classes with the sync system

At the core of the system is the interface `ISyncManaged`, which represents a class that to be synchronised with the client or saved.
At the core of the system are the `ISyncManaged` and `ISyncAnnotated` interfaces, which allow for their fields to have sync annotations..
All block entities which should be synchronised or saved must extend the abstract class `ManagedSyncBlockEntity`.

`ISyncManaged` should be used for classes with any form of persistent state, and cannot be instantiated or reassigned.<br>
`ISyncAnnotated` should be used for record-like classes that hold data, and must define a no-args constructor.


!!! warning
Block entities that inherit `ManagedSyncBlockEntity` must call `ManagedSyncBlockEntity::updateTick`***every tick*** within their ticker, or they will not be saved.

#### Example of `ISyncManaged` usage
```java
class MySyncObject implements ISyncManaged {
// Any class that directly implements ISyncManaged must have the following:

// Any class that directly implements ISyncManaged must have a SyncDataHolder:
@Getter
protected final SyncDataHolder syncDataHolder = new SyncDataHolder(this);

// ISyncManaged objects should be attached to a parent sync managed object,
// unless the sync managed object is a blockentity
// ISyncManaged classes must implement a getter for their parent sync object
@Getter
private final MetaMachine parentSyncObject;

@SaveField
@SyncToClient
private BlockPos syncPos;

@SaveField
@SyncToClient
private ExampleSyncAnnotated syncAnnotatedField;

public MySyncObject(MetaMachine machine) {
this.parentSyncObject = machine;
}

public void doChanges() {

syncPos = BlockPos.ZERO;
// Client sync fields do not update automatically.
getSyncDataHolder().markClientSyncFieldDirty("syncPos")

syncAnnotatedField.someValue = 10;
/*
* Because ISyncAnnotated classes do not manage their own sync state,
* updating a field in an ISyncAnnotated class requires the parent field to be marked as changed.
*/
getSyncDataHolder().markClientSyncFieldDirty("syncAnnotatedField");
}

/**
* Function called when the SyncDataHolder requests a rerender
*/
void scheduleRenderUpdate();

/**
* Function called to notify the server that this object has been updated and must be synced to clients
*/
void markAsChanged();
private static class ExampleSyncAnnotated implements ISyncAnnotated {
public int someValue = 0;
}
}
```

Expand Down
7 changes: 3 additions & 4 deletions docs/content/Development/General-Topics/Tick-Updates.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ Lets look at how we implement it in `QuantumChest`.
@Override
public void onLoad() {
super.onLoad();
if (getLevel() instanceof ServerLevel serverLevel) {
// you cant call ItemTransferHelper.getItemTransfer while chunk is loading, so lets defer it next tick.
serverLevel.getServer().tell(new TickTask(0, this::updateAutoOutputSubscription));
}
// you cant call ItemTransferHelper.getItemTransfer while chunk is loading, so lets defer it next tick.
scheduleForNextServerTick(this::updateAutoOutputSubscription);
// add a listener to listen the changes of inner inventory. (for ex, if inventory not empty anymore, we may need to unpdate logic)
exportItemSubs = cache.addChangedListener(this::updateAutoOutputSubscription);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition;
import com.gregtechceu.gtceu.api.machine.feature.*;
import com.gregtechceu.gtceu.api.sync_system.ManagedSyncBlockEntity;
import com.gregtechceu.gtceu.common.machine.owner.MachineOwner;
import com.gregtechceu.gtceu.utils.ExtendedUseOnContext;
import com.gregtechceu.gtceu.utils.GTUtil;
Expand Down Expand Up @@ -392,13 +391,9 @@ public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, Block
if (blockEntityType == getDefinition().getBlockEntityType()) {
if (!level.isClientSide) {
return (pLevel, pPos, pState, pTile) -> {
pTile.setChanged();
if (pTile instanceof MetaMachine metaMachine) {
metaMachine.serverTick();
}
if (pTile instanceof ManagedSyncBlockEntity syncObj) {
syncObj.updateTick();
}
};
} else {
return (pLevel, pPos, pState, pTile) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet;
import com.gregtechceu.gtceu.api.pipenet.PipeNet;
import com.gregtechceu.gtceu.api.registry.registrate.provider.GTBlockstateProvider;
import com.gregtechceu.gtceu.api.sync_system.ManagedSyncBlockEntity;
import com.gregtechceu.gtceu.client.model.pipe.PipeModel;
import com.gregtechceu.gtceu.common.data.GTItems;
import com.gregtechceu.gtceu.common.data.GTMaterialBlocks;
Expand Down Expand Up @@ -447,9 +446,6 @@ public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, Block
if (pTile instanceof IPipeNode<?, ?> pipeNode) {
pipeNode.serverTick();
}
if (pTile instanceof ManagedSyncBlockEntity syncObj) {
syncObj.updateTick();
}
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ default void scheduleNeighborShapeUpdate() {
level.getBlockState(pos).updateNeighbourShapes(level, pos, Block.UPDATE_ALL);
}

void markAsChanged();

default boolean isRemote() {
return getLevel() == null ? GTCEu.isClientThread() : getLevel().isClientSide;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ public void unsubscribe(@Nullable TickableSubscription current) {
}

public final void serverTick() {
super.serverTick();
if (!waitingToAdd.isEmpty()) {
serverTicks.addAll(waitingToAdd);
waitingToAdd.clear();
Expand Down Expand Up @@ -308,13 +309,6 @@ public boolean triggerEvent(int id, int para) {
return false;
}

@Override
public void setChanged() {
if (getLevel() != null) {
getLevel().blockEntityChanged(getBlockPos());
}
}

//////////////////////////////////////
// ******* Interaction *******//
//////////////////////////////////////
Expand Down Expand Up @@ -358,14 +352,14 @@ public UITexture getPipeTexture(boolean isBlock) {
if (player == null) return Pair.of(null, InteractionResult.PASS);

// Prioritize covers
var cover = getCoverContainer().getCoverAtSide(context.getClickedFace());
CoverBehavior cover = getCoverContainer().getCoverAtSide(context.getGridSide());
if (cover != null) {
var result = cover.onToolClick(context);
if (result.getSecond() != InteractionResult.PASS) return result;

if (toolType.contains(GTToolType.CROWBAR) && !isRemote()) {
if (toolType.contains(GTToolType.CROWBAR)) {
getCoverContainer().removeCover(context.getGridSide(), player);
return Pair.of(GTToolType.CROWBAR, InteractionResult.SUCCESS);
return Pair.of(GTToolType.CROWBAR, InteractionResult.sidedSuccess(isRemote()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public interface ICoverable extends ITickSubscription, ISyncManaged, ICopyable {

IGregtechBlockEntity getHolder();

@Override
default ISyncManaged getParentSyncObject() {
return getHolder();
}

default Level getLevel() {
return getHolder().getLevel();
}
Expand All @@ -65,18 +70,10 @@ default void notifyBlockUpdate() {
getHolder().notifyBlockUpdate();
}

default void scheduleRenderUpdate() {
getHolder().notifyBlockUpdate();
}

default void scheduleNeighborShapeUpdate() {
getHolder().scheduleNeighborShapeUpdate();
}

default void markAsChanged() {
getHolder().markAsChanged();
}

@Nullable
@Override
default TickableSubscription subscribeServerTick(Runnable runnable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,9 @@ public CoverBehavior(CoverDefinition definition, ICoverable coverHolder, Directi
this.attachedSide = attachedSide;
}

//////////////////////////////////////
// ***** Initialization ******//
//////////////////////////////////////
public void scheduleRenderUpdate() {
coverHolder.scheduleRenderUpdate();
}

@Override
public void markAsChanged() {
coverHolder.markAsChanged();
public ISyncManaged getParentSyncObject() {
return coverHolder;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,7 @@ private void loadFilterFromItem() {
}

@Override
public void markAsChanged() {
container.markAsChanged();
}

@Override
public void scheduleRenderUpdate() {
container.scheduleRenderUpdate();
public @Nullable ISyncManaged getParentSyncObject() {
return container;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.level.Level;

import org.jetbrains.annotations.Nullable;

import java.util.function.BooleanSupplier;

/**
Expand All @@ -22,7 +24,7 @@ public class ConditionalSubscriptionHandler {
private final Runnable runnable;
private final BooleanSupplier condition;

private TickableSubscription subscription;
private @Nullable TickableSubscription subscription;

public ConditionalSubscriptionHandler(ITickSubscription handler, Runnable runnable, BooleanSupplier condition) {
this.handler = handler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.TickTask;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
Expand Down Expand Up @@ -130,6 +132,8 @@ public class MetaMachine extends ManagedSyncBlockEntity implements IGregtechBloc
private final long offset = GTValues.RNG.nextInt(20);

@Getter
@SaveField
@SyncToClient
protected final MachineTraitHolder traitHolder;

private final List<TickableSubscription> serverTicks;
Expand All @@ -155,9 +159,10 @@ public void load(CompoundTag tag) {
}

/**
* Called when this machine is loaded. The entire world is not loaded when this method is called.
* To schedule code to run on the first full world tick, do
* {@code serverLevel.getServer().tell(new TickTask(0, CALLBACK))}
* Called when this machine is loaded.<br>
* On the server side, the entire world may not be loaded when this method is called.<br>
* On the client side, this method is called before this machine's data has been received.<br>
* To schedule code to run on the first full world tick, see {@link #scheduleForNextServerTick(Runnable)}
*/
@MustBeInvokedByOverriders
public void onLoad() {
Expand All @@ -171,6 +176,18 @@ public void onLoad() {
}
}

/**
* Schedules a callback to be executed on the next server tick. Only works on the server-side. <br>
* Should be called from methods such as {@link #onLoad()}, when the world may not be fully loaded.
*
* @param runnable The callback to execute
*/
public final void scheduleForNextServerTick(Runnable runnable) {
if (getLevel() instanceof ServerLevel serverLevel) {
serverLevel.getServer().tell(new TickTask(0, runnable));
}
}

@Override
public final void setRemoved() {
super.setRemoved();
Expand Down Expand Up @@ -234,6 +251,7 @@ public void unsubscribe(@Nullable TickableSubscription current) {

@ApiStatus.Internal
public final void serverTick() {
super.serverTick();
executeTick();
}

Expand Down Expand Up @@ -397,14 +415,14 @@ public <T extends MachineTrait> Optional<T> getTraitOptional(MachineTraitType<T>
Pair<@Nullable GTToolType, InteractionResult> result = null;

// Prioritize covers
var cover = getCoverContainer().getCoverAtSide(context.getClickedFace());
CoverBehavior cover = getCoverContainer().getCoverAtSide(context.getGridSide());
if (cover != null) {
result = cover.onToolClick(context);
if (result.getSecond() != InteractionResult.PASS) return result;

if (toolType.contains(GTToolType.CROWBAR) && !isRemote()) {
if (toolType.contains(GTToolType.CROWBAR)) {
getCoverContainer().removeCover(context.getGridSide(), player);
return Pair.of(GTToolType.CROWBAR, InteractionResult.SUCCESS);
return Pair.of(GTToolType.CROWBAR, InteractionResult.sidedSuccess(isRemote()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.chat.Component;
import net.minecraft.server.TickTask;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
Expand Down Expand Up @@ -91,10 +90,9 @@ protected NotifiableFluidTank createWaterTank() {
@Override
public void onLoad() {
super.onLoad();
if (getLevel() instanceof ServerLevel serverLevel) {
serverLevel.getServer().tell(new TickTask(0, this::updateAutoOutputSubscription));
}
updateSteamSubscription();

scheduleForNextServerTick(this::updateAutoOutputSubscription);
scheduleForNextServerTick(this::updateSteamSubscription);
steamTankSubs = steamTank.addChangedListener(this::updateAutoOutputSubscription);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ public MetaMachine getMachine() {
return machine;
}

@Override
public @Nullable ISyncManaged getParentSyncObject() {
return getMachine();
}

/**
* A list containing the machine classes which this trait can be attached to.
* If this trait is being attached to a machine class that does not conform to any of the list elements, an
Expand Down Expand Up @@ -117,14 +122,10 @@ public void setRenderState(MachineRenderState state) {
getMachine().setRenderState(state);
}

public void scheduleRenderUpdate() {
getMachine().scheduleRenderUpdate();
}

/**
* Called when the machine is loaded. The entire world is not loaded when this method is called.
* To schedule code to run on the first full world tick, do
* {@code serverLevel.getServer().tell(new TickTask(0, CALLBACK))}
* {@code getMachine().scheduleForNextServerTick(callback)}
*/
public void onMachineLoad() {}

Expand Down
Loading
Loading