diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockControllerMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockControllerMachine.java index 89b0be5835f..762e42a214b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockControllerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockControllerMachine.java @@ -8,6 +8,7 @@ import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.machine.property.GTMachineModelProperties; +import com.gregtechceu.gtceu.api.machine.trait.MultiblockMachineTrait; import com.gregtechceu.gtceu.api.pattern.BlockPattern; import com.gregtechceu.gtceu.api.pattern.MultiblockState; import com.gregtechceu.gtceu.api.pattern.MultiblockWorldSavedData; @@ -123,6 +124,12 @@ public void onStructureFormed() { } part.addedToController(this); } + updatePartPositions(); + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof MultiblockMachineTrait multiblockMachineTrait) + multiblockMachineTrait.onStructureFormed(); + } } /** @@ -147,6 +154,11 @@ public void onStructureInvalid() { parallelHatch = null; parts.clear(); updatePartPositions(); + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof MultiblockMachineTrait multiblockMachineTrait) + multiblockMachineTrait.onStructureInvalid(); + } } /** diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java index 1f12ceaeec1..1e7eb18063a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java @@ -1,6 +1,12 @@ package com.gregtechceu.gtceu.api.machine.trait; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.sync_system.data_transformers.ValueTransformer; +import com.gregtechceu.gtceu.api.sync_system.data_transformers.ValueTransformers; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -10,6 +16,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; public final class MachineTraitHolder { @@ -18,10 +25,13 @@ public final class MachineTraitHolder { private final List traits; private final Map, List> traitsByType; + private final Map traitsToSave; + public MachineTraitHolder(MetaMachine machine) { this.machine = machine; this.traits = new ObjectArrayList<>(); this.traitsByType = new Object2ObjectOpenHashMap<>(); + this.traitsToSave = new Object2ObjectOpenHashMap<>(); } public @UnmodifiableView List getAllTraits() { @@ -40,6 +50,28 @@ public void attachTrait(MachineTrait trait) { traits.add(trait); } + /** + * Registers a trait to be synced/saved. + * Do not register a trait to be synced and also store that trait as a syncable machine field, otherwise the trait + * data will be duplicated. Use only one sync method. + * + * @param traitName Unique identifier for this trait. + * @param trait The trait to register + */ + public MachineTraitHolder syncTrait(String traitName, MachineTrait trait) { + if (trait.machine != machine) throw new IllegalArgumentException("Trait does not belong to this machine."); + if (traitsToSave.containsKey(traitName)) + throw new IllegalArgumentException("Attempted to register duplicate trait save key \"" + traitName + "\""); + traitsToSave.put(traitName, trait); + return this; + } + + @SuppressWarnings("unchecked") + public @Nullable T getSyncTrait(String traitName) { + MachineTrait trait = traitsToSave.get(traitName); + return trait == null ? null : (T) trait; + } + /** * Gets the first trait with the specified type. */ @@ -62,4 +94,39 @@ public Optional getTraitOptional(MachineTraitType if (traitList == null) return List.of(); return Collections.unmodifiableList(traitList); } + + private static class MachineTraitHolderTransformer implements ValueTransformer { + + @Override + public Tag serializeNBT(MachineTraitHolder value, TransformerContext context) { + CompoundTag tag = new CompoundTag(); + + value.traitsToSave.forEach((k, v) -> tag.put(k, + v.getSyncDataHolder().serializeNBT(context.isClientSync(), context.isClientFullSyncUpdate()))); + + return tag; + } + + @Override + public @Nullable MachineTraitHolder deserializeNBT(Tag tag, TransformerContext context) { + var traitHolder = Objects.requireNonNull(context.currentValue()); + var compoundTag = (CompoundTag) tag; + + for (var key : compoundTag.getAllKeys()) { + var trait = traitHolder.getSyncTrait(key); + if (trait == null) { + GTCEu.LOGGER.warn("Attempted to deserialise syncable trait '{}', but no syncable trait has that ID", + key); + continue; + } + trait.getSyncDataHolder().deserializeNBT(compoundTag.getCompound("key"), context.isClientSync()); + } + + return null; + } + } + + static { + ValueTransformers.registerTransformer(MachineTraitHolder.class, new MachineTraitHolderTransformer()); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MultiblockMachineTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MultiblockMachineTrait.java new file mode 100644 index 00000000000..4070537b988 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MultiblockMachineTrait.java @@ -0,0 +1,14 @@ +package com.gregtechceu.gtceu.api.machine.trait; + +import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine; + +public abstract class MultiblockMachineTrait extends MachineTrait { + + public MultiblockMachineTrait(MultiblockControllerMachine multiMachine) { + super(multiMachine); + } + + public void onStructureFormed() {} + + public void onStructureInvalid() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/SyncDataHolder.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/SyncDataHolder.java index c16d2630b29..03f195340e3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/SyncDataHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/SyncDataHolder.java @@ -59,7 +59,7 @@ public CompoundTag serializeNBT(boolean writeClientFields, boolean fullSync) { CompoundTag tag = new CompoundTag(); for (var field : fieldsToSerialize) { if (shouldSerializeField(field, writeClientFields, fullSync)) { - Tag nbtValue = serializeField(holder, field, writeClientFields); + Tag nbtValue = serializeField(holder, field, writeClientFields, fullSync); tag.put(field.nbtSaveKey, nbtValue); } } @@ -101,7 +101,7 @@ public void deserializeNBT(CompoundTag tag, boolean readingClientFields) { @SuppressWarnings("unchecked") private static Tag serializeField(ISyncManaged holder, FieldSyncData field, - boolean writeClientFields) { + boolean writeClientFields, boolean fullSync) { Object currentValue = field.handle.get(holder); if (!field.isSyncManaged && currentValue == null) { @@ -115,7 +115,7 @@ private static Tag serializeField(ISyncManaged holder, FieldSyncData field, if (field.transformer != null) { return ((ValueTransformer) field.transformer).serializeNBT(currentValue, new ValueTransformer.TransformerContext<>(holder, field.type, currentValue, field.fieldName, - writeClientFields)); + writeClientFields, fullSync)); } else if (currentValue instanceof ISyncManaged syncObj) { return syncObj.getSyncDataHolder().serializeNBT(writeClientFields); } else { @@ -150,7 +150,7 @@ private static void deserializeField(ISyncManaged holder, FieldSyncData field, try { var current = field.handle.get(holder); Object result = transformer.deserializeNBT(savedValue, new ValueTransformer.TransformerContext<>( - holder, field.type, current, field.fieldName, readingClientFields)); + holder, field.type, current, field.fieldName, readingClientFields, false)); if (result != current) { field.handle.set(holder, result); } @@ -175,4 +175,20 @@ private static void deserializeField(ISyncManaged holder, FieldSyncData field, GTCEu.LOGGER.error(e); } } + + public static class SyncManagedTransformer implements ValueTransformer { + + @Override + public Tag serializeNBT(ISyncManaged value, TransformerContext context) { + return value.getSyncDataHolder().serializeNBT(context.isClientSync(), context.isClientFullSyncUpdate()); + } + + @Override + public @Nullable ISyncManaged deserializeNBT(Tag tag, TransformerContext context) { + ISyncManaged syncManaged = context.currentValue(); + Objects.requireNonNull(syncManaged).getSyncDataHolder().deserializeNBT((CompoundTag) tag, + context.isClientSync()); + return syncManaged; + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformer.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformer.java index 22a83bc1f86..95128ec835c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformer.java @@ -25,11 +25,12 @@ public interface ValueTransformer { * @param fieldName The name of the field being serialized, or a string denoting the current sync context if not * being invoked directly on a field. * @param isClientSync Whether NBT is currently being generated as part of a sync update to the client, not as NBT - * being - * written to the server save. + * being written to the server save. + * */ record TransformerContext(@NotNull ISyncManaged holder, @NotNull TypeDeclaration type, - @Nullable U currentValue, @Nullable String fieldName, boolean isClientSync) {} + @Nullable U currentValue, @Nullable String fieldName, boolean isClientSync, + boolean isClientFullSyncUpdate) {} /** * Casts a given NBT tag to a specific tag type, throwing an error if the tag cannot be casted. diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformers.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformers.java index 5a123341f30..09d3114fa7e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformers.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/ValueTransformers.java @@ -6,6 +6,8 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.GTRecipeType; import com.gregtechceu.gtceu.api.registry.GTRegistries; +import com.gregtechceu.gtceu.api.sync_system.ISyncManaged; +import com.gregtechceu.gtceu.api.sync_system.SyncDataHolder; import com.gregtechceu.gtceu.api.sync_system.TypeDeclaration; import com.gregtechceu.gtceu.api.sync_system.data_transformers.collections.ListTransformer; import com.gregtechceu.gtceu.api.sync_system.data_transformers.collections.MapTransformer; @@ -177,6 +179,7 @@ public static void registerTransformerSupplier(Class type, Supplier getInnerElemContext(@Nullable T e ValueTransformer.TransformerContext> parentContext) { return new TransformerContext<>(parentContext.holder(), parentContext.type().getGenericTypeArgs()[0], elem, parentContext.fieldName() + "[element]", - parentContext.isClientSync()); + parentContext.isClientSync(), parentContext.isClientFullSyncUpdate()); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/MapTransformer.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/MapTransformer.java index b66985a169e..56bc98b7644 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/MapTransformer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/MapTransformer.java @@ -48,7 +48,7 @@ private ValueTransformer.TransformerContext getInnerKeyContext(@Nullable K ke ValueTransformer.TransformerContext> parentContext) { return new TransformerContext<>(parentContext.holder(), parentContext.type().getGenericTypeArgs()[0], key, parentContext.fieldName() + "[key]", - parentContext.isClientSync()); + parentContext.isClientSync(), parentContext.isClientFullSyncUpdate()); } private ValueTransformer.TransformerContext getInnerValueContext(@Nullable V value, @@ -56,7 +56,7 @@ private ValueTransformer.TransformerContext getInnerValueContext(@Nullable V return new TransformerContext<>(parentContext.holder(), parentContext.type().getGenericTypeArgs()[1], value, parentContext.fieldName() + "[value]", - parentContext.isClientSync()); + parentContext.isClientSync(), parentContext.isClientFullSyncUpdate()); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/ObjectArrayTransformer.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/ObjectArrayTransformer.java index 1f665105fe0..85eb9fe50b6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/ObjectArrayTransformer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/ObjectArrayTransformer.java @@ -23,7 +23,7 @@ private ValueTransformer.TransformerContext getInnerElemContext(@Nullable T e ValueTransformer.TransformerContext parentContext) { return new TransformerContext<>(parentContext.holder(), parentContext.type().getArrayComponentType(), elem, parentContext.fieldName() + "[element]", - parentContext.isClientSync()); + parentContext.isClientSync(), parentContext.isClientFullSyncUpdate()); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/SetTransformer.java b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/SetTransformer.java index 94c03417653..12f80e4b9da 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/SetTransformer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/sync_system/data_transformers/collections/SetTransformer.java @@ -33,7 +33,7 @@ private ValueTransformer.TransformerContext getInnerElemContext(@Nullable T e ValueTransformer.TransformerContext> parentContext) { return new TransformerContext<>(parentContext.holder(), parentContext.type().getGenericTypeArgs()[0], elem, parentContext.fieldName() + "[element]", - parentContext.isClientSync()); + parentContext.isClientSync(), parentContext.isClientSync()); } @Override