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
6 changes: 6 additions & 0 deletions src/main/java/com/modularmc/synceddata/SyncedData.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.modularmc.synceddata;

import com.modularmc.synceddata.api.sync_system.SyncedComponents;
import com.modularmc.synceddata.utils.FormattingUtil;

import net.minecraft.client.Minecraft;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLEnvironment;
Expand All @@ -30,6 +32,10 @@ public class SyncedData {
public static final Path SYNCED_FOLDER = getGameDir().resolve("synced");
private static final Identifier TEMPLATE_LOCATION = Identifier.fromNamespaceAndPath(MOD_ID, "");

public SyncedData(IEventBus bus) {
SyncedComponents.COMPONENTS.register(bus);
}

public static Identifier id(String path) {
if (path.isBlank()) {
return TEMPLATE_LOCATION;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package com.modularmc.synceddata.api.sync_system;

import com.modularmc.synceddata.api.sync_system.holder.SyncDataHolder;

public interface ISyncManaged {

SyncDataHolder getSyncDataHolder();

/**
* Function called when a synced field 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();
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
package com.modularmc.synceddata.api.sync_system;

import com.modularmc.synceddata.api.blockentity.BlockEntityCreationInfo;
import com.modularmc.synceddata.api.sync_system.holder.SyncDataHolder;

import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;

import com.mojang.serialization.MapCodec;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

public abstract class ManagedSyncBlockEntity extends BlockEntity implements ISyncManaged {

@Getter
protected final SyncDataHolder syncDataHolder = new SyncDataHolder(this);
@Getter
@Setter
private boolean isDirty;

public ManagedSyncBlockEntity(BlockEntityCreationInfo info) {
super(info.type(), info.pos(), info.state());
Expand All @@ -30,16 +40,78 @@ public ManagedSyncBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState
}

@Override
public final void markAsChanged() {
isDirty = true;
protected void saveAdditional(ValueOutput output) {
super.saveAdditional(output);
var registries = Objects.requireNonNull(getLevel()).registryAccess();
CompoundTag tag = getSyncDataHolder().serializeToSaveNBT(registries)
.merge(getSyncDataHolder().serializeToItemNBT(registries));
if (!tag.isEmpty()) {
output.store("synced", CompoundTag.CODEC, tag);
}
}

@Override
public void loadAdditional(ValueInput input) {
super.loadAdditional(input);
if (getLevel() == null) return;
var registries = getLevel().registryAccess();
boolean clientSide = getLevel() instanceof ClientLevel;
input.read(MapCodec.assumeMapUnsafe(CompoundTag.CODEC)).ifPresent(fullTag -> {
var synced = fullTag.getCompound("synced").orElse(new CompoundTag());
if (!synced.isEmpty()) {
getSyncDataHolder().deserializeNBT(registries, synced, clientSide);
if (!clientSide) {
getSyncDataHolder().deserializeItemNBT(registries, synced);
}
}
});
}

@Override
protected void collectImplicitComponents(DataComponentMap.Builder components) {
super.collectImplicitComponents(components);
var registries = Objects.requireNonNull(getLevel()).registryAccess();
components.set(SyncedComponents.BLOCK_ITEM_DATA.get(),
getSyncDataHolder().serializeToItemNBT(registries));
}

@Override
protected void applyImplicitComponents(DataComponentGetter components) {
super.applyImplicitComponents(components);
var data = components.get(SyncedComponents.BLOCK_ITEM_DATA.get());
if (data != null && getLevel() != null) {
getSyncDataHolder().deserializeItemNBT(getLevel().registryAccess(), data);
}
}

@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
getSyncDataHolder().resyncAllFields();
return getSyncDataHolder().serializeFullClientSyncNBT(registries);
}

@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this,
(be, r) -> ((ManagedSyncBlockEntity) be).syncDataHolder.getPendingChanges());
}

@Override
public void markAsChanged() {
setChanged();
}

public final void updateTick() {
setChanged();
if (isDirty) {
Objects.requireNonNull(getLevel()).sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(),
Block.UPDATE_CLIENTS);
isDirty = false;
if (getLevel() instanceof ServerLevel serverLevel) {
if (syncDataHolder.scanAndMarkChanges(serverLevel.registryAccess())) {
serverLevel.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(),
Block.UPDATE_CLIENTS);
}
}
}

public final void handleClientUpdate(HolderLookup.Provider registries, CompoundTag tag) {
syncDataHolder.applyServerUpdate(registries, tag);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.modularmc.synceddata.api.sync_system;

import com.modularmc.synceddata.SyncedData;

import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.neoforged.neoforge.registries.DeferredRegister;

import java.util.function.Supplier;

public class SyncedComponents {

public static final DeferredRegister<DataComponentType<?>> COMPONENTS = DeferredRegister.create(Registries.DATA_COMPONENT_TYPE, SyncedData.MOD_ID);

public static final Supplier<DataComponentType<CompoundTag>> BLOCK_ITEM_DATA = COMPONENTS.register("block_item_data",
() -> DataComponentType.<CompoundTag>builder().persistent(CompoundTag.CODEC).build());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClientFieldChangeListener {

String fieldName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ItemSave {

String nbtKey() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RerenderOnChanged {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SaveField {

String nbtKey() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SyncBoth {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SyncToClient {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.modularmc.synceddata.api.sync_system.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SyncToServer {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.modularmc.synceddata.api.sync_system.holder;

import com.modularmc.synceddata.api.sync_system.ISyncManaged;
import com.modularmc.synceddata.api.sync_system.SyncedComponents;

import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;

import lombok.Getter;

/**
* Wraps an ItemStack with the sync annotation system.
* All annotated field data uses {@link SyncedComponents#BLOCK_ITEM_DATA} DataComponent.
*/
public final class ItemSyncHolder implements ISyncManaged {

@Getter
private final SyncDataHolder syncDataHolder;

public ItemSyncHolder(ISyncManaged owner) {
this.syncDataHolder = new SyncDataHolder(owner);
}

public void saveToStack(ItemStack stack, HolderLookup.Provider registries) {
syncDataHolder.applyToItemStack(stack, registries);
}

public void loadFromStack(ItemStack stack, HolderLookup.Provider registries, boolean clientSide) {
syncDataHolder.loadFromItemStack(stack, registries);
if (clientSide) {
var data = stack.get(SyncedComponents.BLOCK_ITEM_DATA.get());
if (data != null) {
syncDataHolder.deserializeNBT(registries, data, true);
}
}
}

public boolean scanChanges(HolderLookup.Provider registries) {
return syncDataHolder.scanAndMarkChanges(registries);
}

public void flushToStack(ItemStack stack, HolderLookup.Provider registries) {
CompoundTag pending = syncDataHolder.getPendingChanges();
if (!pending.isEmpty()) {
var existing = stack.get(SyncedComponents.BLOCK_ITEM_DATA.get());
stack.set(SyncedComponents.BLOCK_ITEM_DATA.get(),
existing != null ? existing.merge(pending) : pending);
}
}

public void applyServerUpdate(HolderLookup.Provider registries, CompoundTag tag) {
syncDataHolder.applyServerUpdate(registries, tag);
}

@Override
public void scheduleRenderUpdate() {}

@Override
public void markAsChanged() {}
}
Loading
Loading