feat: 测试下来,1.21.1总体逻辑没问题

This commit is contained in:
叁玖领域 2026-05-11 23:29:08 +08:00
parent fbaeaf857b
commit 805d880ed9
34 changed files with 566 additions and 212 deletions

View File

@ -24,8 +24,8 @@ dependencies {
transitive = false transitive = false
} }
// fabric and neoforge both bundle mixinextras, so it is safe to use it in common // fabric and neoforge both bundle mixinextras, so it is safe to use it in common
compileOnly group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5' implementation group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.4.1'
annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.3.5' annotationProcessor group: 'io.github.llamalad7', name: 'mixinextras-common', version: '0.4.1'
} }
configurations { configurations {
commonJava { commonJava {

View File

@ -587,6 +587,7 @@ public class WheelWidget extends AbstractWidget {
PoseStack poseStack = guiGraphics.pose(); PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose(); poseStack.pushPose();
setupRingShader(centerX, centerY, innerRadius, outerRadius);
Tesselator tesselator = Tesselator.getInstance(); Tesselator tesselator = Tesselator.getInstance();
// 计算足够大的绘制区域来覆盖整个环形基于外半径 // 计算足够大的绘制区域来覆盖整个环形基于外半径
@ -604,9 +605,8 @@ public class WheelWidget extends AbstractWidget {
buffer.addVertex(matrix, x2, y2, -300).setColor(color); buffer.addVertex(matrix, x2, y2, -300).setColor(color);
buffer.addVertex(matrix, x2, y1, -300).setColor(color); buffer.addVertex(matrix, x2, y1, -300).setColor(color);
setupRingShader(centerX, centerY, innerRadius, outerRadius);
BufferUploader.drawWithShader(buffer.build()); BufferUploader.drawWithShader(buffer.build());
RenderSystem.setShader(() -> null);
poseStack.popPose(); poseStack.popPose();
} }
@ -721,9 +721,6 @@ public class WheelWidget extends AbstractWidget {
Lib39Shaders.getSelectionShader() Lib39Shaders.getSelectionShader()
.safeGetUniform("Radius") .safeGetUniform("Radius")
.set(radius * guiScale); .set(radius * guiScale);
Lib39Shaders.getSelectionShader()
.safeGetUniform("AntiAliasingRadius")
.set(guiScale); // 根据需要调整
RenderSystem.setShaderColor(1, 1, 1, 1); RenderSystem.setShaderColor(1, 1, 1, 1);
BufferUploader.drawWithShader(Objects.requireNonNull(buffer.build())); BufferUploader.drawWithShader(Objects.requireNonNull(buffer.build()));
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();

View File

@ -4,25 +4,32 @@ import com.mojang.authlib.GameProfile;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis; import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.client.resources.SkinManager;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.block.SkullBlock; import net.minecraft.world.level.block.SkullBlock;
import net.minecraft.world.level.block.WallSkullBlock; import net.minecraft.world.level.block.WallSkullBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RotationSegment; import net.minecraft.world.level.block.state.properties.RotationSegment;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.client.model.DollModel; import top.r3944realms.lib39.client.model.DollModel;
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
import top.r3944realms.lib39.content.block.AbstractDollBlock; import top.r3944realms.lib39.content.block.AbstractDollBlock;
import top.r3944realms.lib39.content.block.WallDollBlock; import top.r3944realms.lib39.content.block.WallDollBlock;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity; import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.util.lang.Pair; import top.r3944realms.lib39.util.lang.Pair;
import java.util.Optional;
/** /**
* The type Doll block entity renderer. * The type Doll block entity renderer.
*/ */
@ -45,16 +52,43 @@ public class DollBlockEntityRenderer implements BlockEntityRenderer<DollBlockEnt
boolean isWall = dollBlock instanceof WallDollBlock; boolean isWall = dollBlock instanceof WallDollBlock;
Direction direction = isWall ? blockState.getValue(WallSkullBlock.FACING) : null; Direction direction = isWall ? blockState.getValue(WallSkullBlock.FACING) : null;
float rotation = isWall ? direction.toYRot() : RotationSegment.convertToDegrees(blockState.getValue(SkullBlock.ROTATION)); float rotation = isWall ? direction.toYRot() : RotationSegment.convertToDegrees(blockState.getValue(SkullBlock.ROTATION));
GameProfile profile = dollBlockEntity.getOwnerProfile(); Pair<PlayerSkin.Model, RenderType> modelAndRenderTypePair = getModelAndRenderTypePair(dollBlockEntity.getOwnerProfile());
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = DollItemRenderer.loadSkin(profile);
poseStack.pushPose(); poseStack.pushPose();
poseStack.translate(0.5, 1.5, 0.5); poseStack.translate(0.5, 1.5, 0.5);
poseStack.scale(1.0F, -1.0F, -1.0F); poseStack.scale(1.0F, -1.0F, -1.0F);
poseStack.mulPose(Axis.YP.rotationDegrees(rotation)); poseStack.mulPose(Axis.YP.rotationDegrees(rotation));
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(resourceLocationBooleanPair.first)); VertexConsumer vertexConsumer = buffer.getBuffer(modelAndRenderTypePair.second);
this.dollModel.slim = resourceLocationBooleanPair.second; this.dollModel.slim = modelAndRenderTypePair.first == PlayerSkin.Model.SLIM;
this.dollModel.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY,-1); this.dollModel.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY,-1);
poseStack.popPose(); poseStack.popPose();
} }
} }
public static @NotNull Pair<PlayerSkin.Model, RenderType> getModelAndRenderTypePair(@Nullable ResolvableProfile profile) {
SkinManager skinmanager = Minecraft.getInstance().getSkinManager();
if (profile == null) {
return Pair.of(PlayerSkin.Model.SLIM,
RenderType.entityTranslucent(DefaultPlayerSkin.getDefaultTexture()));
}
GameProfile gameProfile = profile.gameProfile();
// 如果当前 Profile 没有纹理数据尝试从 DollBlockEntity 缓存获取
if (gameProfile != null && gameProfile.getProperties().isEmpty()
&& gameProfile.getName() != null && !gameProfile.getName().isEmpty()) {
Optional<GameProfile> cached = DollBlockEntity.fetchGameProfile(gameProfile.getName())
.getNow(Optional.empty());
if (cached.isPresent() && !cached.get().getProperties().isEmpty()) {
gameProfile = cached.get();
}
}
if (gameProfile != null && !gameProfile.getProperties().isEmpty()) {
PlayerSkin skin = skinmanager.getInsecureSkin(gameProfile);
return Pair.of(skin.model(), RenderType.entityTranslucent(skin.texture()));
}
return Pair.of(PlayerSkin.Model.SLIM,
RenderType.entityTranslucent(DefaultPlayerSkin.getDefaultTexture()));
}
} }

View File

@ -11,9 +11,11 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.client.resources.PlayerSkin; import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.client.resources.SkinManager; import net.minecraft.client.resources.SkinManager;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ResolvableProfile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.client.model.DollModel; import top.r3944realms.lib39.client.model.DollModel;
@ -62,55 +64,44 @@ public class DollItemRenderer extends BlockEntityWithoutLevelRenderer {
} }
return instance; return instance;
} }
@Override @Override
public void renderByItem(@NotNull ItemStack stack, @NotNull ItemDisplayContext displayContext, @NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer, int packedLight, int packedOverlay) { public void renderByItem(@NotNull ItemStack stack, @NotNull ItemDisplayContext displayContext,
@NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer,
int packedLight, int packedOverlay) {
if (!(stack.getItem() instanceof DollItem)) { if (!(stack.getItem() instanceof DollItem)) {
return; return;
} }
lazyInit(); lazyInit();
GameProfile profile = GameProfileHelper.getProfileFromItemStack(stack);
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = loadSkin(profile); // 直接从 DataComponents.PROFILE 获取 ResolvableProfile
ResourceLocation playerSkin = resourceLocationBooleanPair.first; ResolvableProfile profile = stack.get(DataComponents.PROFILE);
boolean isSlim = resourceLocationBooleanPair.second; Pair<ResourceLocation, PlayerSkin.Model> skinInfo = getSkinInfo(profile);
poseStack.pushPose(); poseStack.pushPose();
VertexConsumer vertexConsumer = buffer.getBuffer( VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(skinInfo.first));
RenderType.entityTranslucent(playerSkin)
);
poseStack.translate(0.5, 2.6, 0.8); poseStack.translate(0.5, 2.6, 0.8);
poseStack.scale(1.8F, -1.8F, -1.8F); poseStack.scale(1.8F, -1.8F, -1.8F);
poseStack.mulPose(Axis.YP.rotationDegrees(180)); poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.dollModel.slim = isSlim;
this.dollModel.renderToBuffer( this.dollModel.slim = skinInfo.second == PlayerSkin.Model.SLIM;
poseStack, this.dollModel.renderToBuffer(poseStack, vertexConsumer, packedLight, packedOverlay, -1);
vertexConsumer,
packedLight,
packedOverlay,
-1
);
poseStack.popPose(); poseStack.popPose();
} }
/** /**
* Load skin pair. * 获取皮肤纹理和模型类型1.21+ 版本
*
* @param profile the profile
* @return the pair
*/ */
public static @NotNull Pair<ResourceLocation,Boolean> loadSkin(GameProfile profile) { public static @NotNull Pair<ResourceLocation, PlayerSkin.Model> getSkinInfo(@NotNull ResolvableProfile profile) {
SkinManager skinManager = Minecraft.getInstance().getSkinManager(); SkinManager skinManager = Minecraft.getInstance().getSkinManager();
ResourceLocation playerSkin;
boolean isSlim; if (profile != null && profile.gameProfile() != null) {
if (profile != null) { PlayerSkin skin = skinManager.getInsecureSkin(profile.gameProfile());
PlayerSkin insecureSkin = skinManager.getInsecureSkin(profile); return Pair.of(skin.texture(), skin.model());
playerSkin = insecureSkin.texture();
isSlim = GameProfileHelper.isSlimArms(profile);
} else {
playerSkin = Lib39.mrl("textures/entity/player/wide/steve.png");
isSlim = false;
} }
return Pair.of(playerSkin, isSlim);
// 默认返回 Steve 皮肤
return Pair.of(DefaultPlayerSkin.getDefaultTexture(), PlayerSkin.Model.SLIM);
} }
} }

View File

@ -4,13 +4,16 @@ import com.mojang.authlib.GameProfile;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -178,9 +181,12 @@ public abstract class AbstractDollBlock extends BaseEntityBlock implements Simpl
ItemStack stack = super.getCloneItemStack(level, pos, state); ItemStack stack = super.getCloneItemStack(level, pos, state);
BlockEntity blockEntity = level.getBlockEntity(pos); BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof DollBlockEntity doll) { if (blockEntity instanceof DollBlockEntity doll) {
GameProfile profile = doll.getOwnerProfile(); ResolvableProfile profile;
if (profile != null) { if (doll.getOwnerProfile() != null) {
GameProfileHelper.saveProfileToItemStack(stack, profile); profile = doll.getOwnerProfile();
if (profile != null) {
GameProfileHelper.saveProfileToItemStack(stack, profile);
}
} }
} }
return stack; return stack;
@ -207,6 +213,19 @@ public abstract class AbstractDollBlock extends BaseEntityBlock implements Simpl
super.createBlockStateDefinition(builder); super.createBlockStateDefinition(builder);
builder.add(WATERLOGGED, POSE); builder.add(WATERLOGGED, POSE);
} }
@Override
public void setPlacedBy(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state,
@Nullable LivingEntity placer, @NotNull ItemStack stack) {
super.setPlacedBy(level, pos, state, placer, stack);
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof DollBlockEntity dollEntity) {
ResolvableProfile profile = GameProfileHelper.getProfileFromItemStack(stack);
if (profile != null) {
dollEntity.setOwner(profile);
}
}
}
/** /**
* 生成自定义掉落物 * 生成自定义掉落物
@ -218,11 +237,14 @@ public abstract class AbstractDollBlock extends BaseEntityBlock implements Simpl
return List.of(); return List.of();
} }
} }
GameProfile profile = dollEntity.getOwnerProfile(); ResolvableProfile profile;
if (profile != null) { if (dollEntity.getOwnerProfile() != null) {
ItemStack instance = Lib39Items.DOLL.get().getDefaultInstance(); profile = dollEntity.getOwnerProfile();
GameProfileHelper.saveProfileToItemStack(instance, profile); if (profile != null) {
return List.of(instance); ItemStack instance = Lib39Items.DOLL.get().getDefaultInstance();
GameProfileHelper.saveProfileToItemStack(instance, profile);
return List.of(instance);
}
} }
return null; return null;
} }

View File

@ -1,62 +1,137 @@
package top.r3944realms.lib39.content.block.blockentity; package top.r3944realms.lib39.content.block.blockentity;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.Services;
import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.core.register.Lib39BlockEntities; import top.r3944realms.lib39.core.register.Lib39BlockEntities;
import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.nbt.NBTReader; import java.time.Duration;
import top.r3944realms.lib39.util.nbt.NBTWriter; import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
/** /**
* The type Doll block entity. * The type Doll block entity.
* 参考 SkullBlockEntity 实现添加缓存机制
*/ */
public class DollBlockEntity extends BlockEntity { public class DollBlockEntity extends BlockEntity {
@Nullable
private GameProfile owner;
/** private static final String TAG_PROFILE = "profile";
* Instantiates a new Doll block entity.
* // 静态缓存 SkullBlockEntity 共享或独立
* @param pos the pos @Nullable
* @param blockState the block state private static Executor mainThreadExecutor;
*/ @Nullable
private static LoadingCache<String, CompletableFuture<Optional<GameProfile>>> profileCacheByName;
@Nullable
private static LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> profileCacheById;
public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = (runnable) -> {
Executor executor = mainThreadExecutor;
if (executor != null) {
executor.execute(runnable);
}
};
@Nullable
private ResolvableProfile owner;
public DollBlockEntity(BlockPos pos, BlockState blockState) { public DollBlockEntity(BlockPos pos, BlockState blockState) {
super(Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(), pos, blockState); super(Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(), pos, blockState);
} }
@Override /**
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { * 初始化缓存 Mod 初始化或服务器启动时调用
super.saveAdditional(tag, registries); */
NBTWriter.of(tag) public static void setup(final Services services, Executor executor) {
.gameProfileIf(GameProfileHelper.TAG_OWN_PROFILE, owner != null, () -> owner); mainThreadExecutor = executor;
final BooleanSupplier cacheUninitialized = () -> profileCacheById == null;
profileCacheByName = CacheBuilder.newBuilder()
.expireAfterAccess(Duration.ofMinutes(10L))
.maximumSize(256L)
.build(new CacheLoader<>() {
@Override
public CompletableFuture<Optional<GameProfile>> load(String username) {
return fetchProfileByName(username, services);
}
});
profileCacheById = CacheBuilder.newBuilder()
.expireAfterAccess(Duration.ofMinutes(10L))
.maximumSize(256L)
.build(new CacheLoader<>() {
@Override
public CompletableFuture<Optional<GameProfile>> load(UUID uuid) {
return fetchProfileById(uuid, services, cacheUninitialized);
}
});
} }
@Override private static CompletableFuture<Optional<GameProfile>> fetchProfileByName(String name, Services services) {
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { return services.profileCache().getAsync(name).thenCompose(optional -> {
super.loadAdditional(tag, registries); LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> cache = profileCacheById;
NBTReader.of(tag) if (cache != null && optional.isPresent()) {
.gameProfile(GameProfileHelper.TAG_OWN_PROFILE, this::setOwner); return cache.getUnchecked(optional.get().getId())
.thenApply(fullProfile -> fullProfile.or(() -> optional));
}
return CompletableFuture.completedFuture(Optional.empty());
});
}
private static CompletableFuture<Optional<GameProfile>> fetchProfileById(UUID uuid, Services services, BooleanSupplier cacheUninitialized) {
return CompletableFuture.supplyAsync(() -> {
if (cacheUninitialized.getAsBoolean()) {
return Optional.empty();
}
var result = services.sessionService().fetchProfile(uuid, true);
return Optional.ofNullable(result).map(com.mojang.authlib.yggdrasil.ProfileResult::profile);
}, Util.backgroundExecutor());
} }
/** /**
* Gets owner profile. * 公开的静态方法异步获取 GameProfile复用 SkullBlockEntity 的缓存或使用自己的
*
* @return the owner profile
*/ */
@Nullable public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(String name) {
public GameProfile getOwnerProfile() { if (profileCacheByName != null && name != null && !name.isEmpty()) {
return this.owner; return profileCacheByName.getUnchecked(name);
}
// 回退到 SkullBlockEntity 的缓存
return SkullBlockEntity.fetchGameProfile(name);
} }
public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID uuid) {
if (profileCacheById != null && uuid != null) {
return profileCacheById.getUnchecked(uuid);
}
return SkullBlockEntity.fetchGameProfile(uuid);
}
public static void clearCache() {
mainThreadExecutor = null;
profileCacheByName = null;
profileCacheById = null;
}
@Nullable
public ResolvableProfile getOwnerProfile() {
return this.owner;
}
@Override @Override
public ClientboundBlockEntityDataPacket getUpdatePacket() { public ClientboundBlockEntityDataPacket getUpdatePacket() {
@ -65,47 +140,57 @@ public class DollBlockEntity extends BlockEntity {
@Override @Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return super.getUpdateTag(registries); CompoundTag tag = super.getUpdateTag(registries);
saveAdditional(tag, registries);
return tag;
} }
/** public void setOwner(@Nullable ResolvableProfile owner) {
* Sets owner.
*
* @param owner the owner
*/
public void setOwner(@Nullable GameProfile owner) {
synchronized (this) { synchronized (this) {
this.owner = owner; this.owner = owner;
} }
this.updateOwnerProfile(); this.updateOwnerProfile();
} }
/**
* Sets owner.
*
* @param ownerName the owner name
*/
public void setOwner(@Nullable String ownerName) { public void setOwner(@Nullable String ownerName) {
setOwner(new GameProfile(Util.NIL_UUID, ownerName)); if (ownerName != null && !ownerName.isEmpty()) {
setOwner(new ResolvableProfile(new GameProfile(Util.NIL_UUID, ownerName)));
}
} }
private void updateOwnerProfile() { private void updateOwnerProfile() {
if (this.owner != null) { if (this.owner != null && !this.owner.isResolved()) {
// 使用 ResolvableProfile 进行异步解析 this.owner.resolve().thenAcceptAsync(resolved -> {
ResolvableProfile resolvableProfile = new ResolvableProfile(this.owner); this.owner = resolved;
if (!resolvableProfile.isResolved()) { this.setChanged();
resolvableProfile.resolve().thenAcceptAsync(resolved -> { // 通知客户端更新
// ResolvableProfile 中获取 GameProfile if (level != null && !level.isClientSide) {
this.owner = resolved.gameProfile(); level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
this.setChanged(); }
if (this.level != null) { }, CHECKED_MAIN_THREAD_EXECUTOR);
this.level.sendBlockUpdated(this.worldPosition, this.getBlockState(), this.getBlockState(), 3); } else {
} this.setChanged();
}, SkullBlockEntity.CHECKED_MAIN_THREAD_EXECUTOR); if (level != null && !level.isClientSide && this.owner != null) {
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
} }
} }
this.setChanged();
} }
} @Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
if (this.owner != null) {
tag.put(TAG_PROFILE, ResolvableProfile.CODEC.encodeStart(NbtOps.INSTANCE, this.owner).getOrThrow());
}
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
if (tag.contains(TAG_PROFILE)) {
ResolvableProfile.CODEC.parse(NbtOps.INSTANCE, tag.get(TAG_PROFILE))
.resultOrPartial(error -> {})
.ifPresent(this::setOwner);
}
}
}

View File

@ -8,6 +8,7 @@ import net.minecraft.world.item.Equipable;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.StandingAndWallBlockItem; import net.minecraft.world.item.StandingAndWallBlockItem;
import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -32,9 +33,12 @@ public class DollItem extends StandingAndWallBlockItem implements Equipable {
@Override @Override
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) { public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
GameProfile profileFromItemStack = GameProfileHelper.getProfileFromItemStack(stack); ResolvableProfile profileFromItemStack = GameProfileHelper.getProfileFromItemStack(stack);
if (profileFromItemStack != null && profileFromItemStack.getName() != null) { if (profileFromItemStack != null) {
tooltipComponents.add(Component.translatable("tooltip.lib39.content.doll.hover.1", profileFromItemStack.getName())); GameProfile gameProfile = profileFromItemStack.gameProfile();
if (gameProfile != null && gameProfile.getName() != null) {
tooltipComponents.add(Component.translatable("tooltip.lib39.content.doll.hover.1", gameProfile.getName()));
}
} }
tooltipComponents.add(Component.translatable("tooltip.lib39.content.doll.hover.2")); tooltipComponents.add(Component.translatable("tooltip.lib39.content.doll.hover.2"));
} }

View File

@ -7,7 +7,7 @@ import net.minecraft.nbt.Tag;
* *
* @param <T> the type parameter * @param <T> the type parameter
*/ */
public interface INBTSerializable <T extends Tag>{ public interface INBTSerializable <T extends Tag> {
/** /**
* Serialize nbt t. * Serialize nbt t.
* *
@ -18,7 +18,7 @@ public interface INBTSerializable <T extends Tag>{
/** /**
* Deserialize nbt. * Deserialize nbt.
* *
* @param var1 the var 1 * @param t the var
*/ */
void deserializeNBT(T var1); void deserializeNBT(T t);
} }

View File

@ -5,23 +5,23 @@ import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ResolvableProfile;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import top.r3944realms.lib39.content.block.AbstractDollBlock;
import top.r3944realms.lib39.content.item.DollItem; import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.util.GameProfileHelper; import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.nbt.NBTReader; import top.r3944realms.lib39.util.nbt.NBTReader;
import tschipp.carryon.client.render.CarriedObjectRender; import tschipp.carryon.client.render.CarriedObjectRender;
import tschipp.carryon.common.carry.CarryOnDataManager; import tschipp.carryon.common.carry.CarryOnDataManager;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* The type Mixin carried object render. * The type Mixin carried object render.
*/ */
@SuppressWarnings("DuplicatedCode")
@Pseudo @Pseudo
@Mixin(value = CarriedObjectRender.class, remap = false) @Mixin(value = CarriedObjectRender.class, remap = false)
public class MixinCarriedObjectRender { public class MixinCarriedObjectRender {
@ -36,9 +36,9 @@ public class MixinCarriedObjectRender {
if (stack.getItem() instanceof DollItem) { if (stack.getItem() instanceof DollItem) {
CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile"); CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile");
AtomicReference<GameProfile> gameProfileAtomicReference = new AtomicReference<>(); AtomicReference<GameProfile> gameProfileAtomicReference = new AtomicReference<>();
NBTReader.of(compound).gameProfile(GameProfileHelper.TAG_OWN_PROFILE, gameProfileAtomicReference::set); NBTReader.of(compound).gameProfile("profile", gameProfileAtomicReference::set);
if (gameProfileAtomicReference.get() != null) { if (gameProfileAtomicReference.get() != null) {
GameProfileHelper.saveProfileToItemStack(stack, gameProfileAtomicReference.get()); GameProfileHelper.saveProfileToItemStack(stack, new ResolvableProfile(gameProfileAtomicReference.get()));
} }
} }
return stack; return stack;
@ -50,16 +50,16 @@ public class MixinCarriedObjectRender {
target = "Ltschipp/carryon/client/render/CarryRenderHelper;renderBakedModel(Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/resources/model/BakedModel;)V" target = "Ltschipp/carryon/client/render/CarryRenderHelper;renderBakedModel(Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/resources/model/BakedModel;)V"
) )
) )
private static ItemStack warpDollItem$2(ItemStack stack, @Local(ordinal = 0) Player player) { private static ItemStack warpDollItem$2(ItemStack tileItem, @Local(ordinal = 0) Player player) {
if (stack.getItem() instanceof DollItem) { if (tileItem.getItem() instanceof DollItem) {
CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile"); CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile");
AtomicReference<GameProfile> gameProfileAtomicReference = new AtomicReference<>(); AtomicReference<GameProfile> gameProfileAtomicReference = new AtomicReference<>();
NBTReader.of(compound).gameProfile(GameProfileHelper.TAG_OWN_PROFILE, gameProfileAtomicReference::set); NBTReader.of(compound).gameProfile("profile", gameProfileAtomicReference::set);
if (gameProfileAtomicReference.get() != null) { if (gameProfileAtomicReference.get() != null) {
GameProfileHelper.saveProfileToItemStack(stack, gameProfileAtomicReference.get()); GameProfileHelper.saveProfileToItemStack(tileItem, new ResolvableProfile(gameProfileAtomicReference.get()));
} }
} }
return stack; return tileItem;
} }
} }

View File

@ -1,4 +1,4 @@
package top.r3944realms.lib39.mixin; package top.r3944realms.lib39.mixin.minecraft;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
@ -8,10 +8,9 @@ import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.core.sync.ILib39SyncDataHolder; import top.r3944realms.lib39.core.sync.ILib39SyncDataHolder;
import top.r3944realms.lib39.core.sync.ISyncData; import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData; import top.r3944realms.lib39.core.sync.NBTEntitySyncData;import top.r3944realms.lib39.platform.Services;
import java.util.UUID; import java.util.UUID;
@ -45,7 +44,7 @@ public abstract class MixinEntity implements ILib39SyncDataHolder {
@WrapMethod(method = "saveWithoutId") @WrapMethod(method = "saveWithoutId")
private CompoundTag wrapSave(CompoundTag compound, @NotNull Operation<CompoundTag> original) { private CompoundTag wrapSave(CompoundTag compound, @NotNull Operation<CompoundTag> original) {
FabricCommonEventHandler.getSyncData2Manager().forEach((id, manager) -> { Services.PLATFORM.getUtilHelper().getSyncData2Manager().forEach((id, manager) -> {
ISyncData<?> o = manager.getSyncMap().get(getUUID()); ISyncData<?> o = manager.getSyncMap().get(getUUID());
if (o instanceof NBTEntitySyncData syncData) { if (o instanceof NBTEntitySyncData syncData) {
saveSyncData(syncData); saveSyncData(syncData);

View File

@ -1,5 +1,6 @@
package top.r3944realms.lib39.platform.services; package top.r3944realms.lib39.platform.services;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder; import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
/** /**
@ -12,4 +13,6 @@ public interface IUtilHelper {
* @return the block registry builder * @return the block registry builder
*/ */
BlockRegistryBuilder getBlockRegistryBuilder(); BlockRegistryBuilder getBlockRegistryBuilder();
SyncData2Manager<?> getSyncData2Manager();
} }

View File

@ -12,9 +12,11 @@ import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.Services;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.Lib39;
@ -22,15 +24,65 @@ import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter; import top.r3944realms.lib39.util.nbt.NBTWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.*;
import java.util.Collection; import java.util.concurrent.CompletableFuture;
import java.util.Objects; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* The type GameProfile helper. * The type GameProfile helper.
*/ */
public class GameProfileHelper { public class GameProfileHelper {
/**
* 异步获取 GameProfile通过玩家名
* 直接复用 SkullBlockEntity 的实现
*/
public static CompletableFuture<Optional<GameProfile>> fetchGameProfileByName(String name) {
return SkullBlockEntity.fetchGameProfile(name);
}
/**
* 异步获取 GameProfile通过 UUID
* 直接复用 SkullBlockEntity 的实现
*/
public static CompletableFuture<Optional<GameProfile>> fetchGameProfileByUUID(UUID uuid) {
return SkullBlockEntity.fetchGameProfile(uuid);
}
/**
* 异步获取 GameProfile自动识别类型
*/
@SuppressWarnings("unchecked")
public static <T> CompletableFuture<Optional<GameProfile>> fetchGameProfile(T identifier) {
if (identifier instanceof UUID uuid) {
return fetchGameProfileByUUID(uuid);
} else if (identifier instanceof String name) {
return fetchGameProfileByName(name);
}
return CompletableFuture.completedFuture(Optional.empty());
}
/**
* ItemStack 异步获取完整的 GameProfile
* 需要配合 ResolvableProfile 使用
*/
public static CompletableFuture<Optional<GameProfile>> fetchProfileFromItemStack(@NotNull ItemStack stack) {
ResolvableProfile resolvable = stack.get(DataComponents.PROFILE);
if (resolvable == null) {
return CompletableFuture.completedFuture(Optional.empty());
}
// 如果已经有 UUID直接用 SkullBlockEntity 的方法
GameProfile profile = resolvable.gameProfile();
if (profile != null && profile.getId() != null) {
return fetchGameProfileByUUID(profile.getId());
}
// 否则异步解析
return resolvable.resolve().thenApply(resolved ->
Optional.ofNullable(resolved.gameProfile())
);
}
/** /**
* Client Only Class * Client Only Class
*/ */
@ -296,7 +348,7 @@ public class GameProfileHelper {
* @return the profile from item stack * @return the profile from item stack
*/ */
@Nullable @Nullable
public static GameProfile getProfileFromItemStack(ItemStack stack) { public static ResolvableProfile getProfileFromItemStack(@NotNull ItemStack stack) {
if (stack.isEmpty()) { if (stack.isEmpty()) {
return null; return null;
} }
@ -304,7 +356,7 @@ public class GameProfileHelper {
if (stack.isEmpty()) return null; if (stack.isEmpty()) return null;
ResolvableProfile profile = stack.get(DataComponents.PROFILE); ResolvableProfile profile = stack.get(DataComponents.PROFILE);
return profile != null ? profile.gameProfile() : null; return profile != null ? profile : null;
} }
/** /**
@ -313,13 +365,13 @@ public class GameProfileHelper {
* @param stack the stack * @param stack the stack
* @param profile the profile * @param profile the profile
*/ */
public static void saveProfileToItemStack(@NotNull ItemStack stack, @Nullable GameProfile profile) { public static void saveProfileToItemStack(@NotNull ItemStack stack, @Nullable ResolvableProfile profile) {
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
if (profile == null) { if (profile == null) {
stack.remove(DataComponents.PROFILE); stack.remove(DataComponents.PROFILE);
} else { } else {
stack.set(DataComponents.PROFILE, new ResolvableProfile(profile)); stack.set(DataComponents.PROFILE, profile);
} }
} }

View File

@ -430,31 +430,67 @@ public class NBTReader {
String name = null; String name = null;
UUID uuid = null; UUID uuid = null;
// 支持 "Name" "name"
if (tag.contains("Name", CompoundTag.TAG_STRING)) { if (tag.contains("Name", CompoundTag.TAG_STRING)) {
name = tag.getString("Name"); name = tag.getString("Name");
} else if (tag.contains("name", CompoundTag.TAG_STRING)) {
name = tag.getString("name");
} }
// 支持 "Id" "id"
if (tag.hasUUID("Id")) { if (tag.hasUUID("Id")) {
uuid = tag.getUUID("Id"); uuid = tag.getUUID("Id");
} else if (tag.hasUUID("id")) {
uuid = tag.getUUID("id");
} }
try { try {
GameProfile profile = new GameProfile(uuid, name); GameProfile profile = new GameProfile(uuid, name);
if (tag.contains("Properties", CompoundTag.TAG_COMPOUND)) { // 支持 "Properties" "properties"支持 COMPOUND LIST 格式
CompoundTag propertiesTag = tag.getCompound("Properties"); String propertiesKey = tag.contains("Properties", CompoundTag.TAG_COMPOUND) ? "Properties"
: (tag.contains("properties", CompoundTag.TAG_COMPOUND) ? "properties" : null);
// 如果没有 COMPOUND尝试 LIST 格式
if (propertiesKey == null) {
String listKey = tag.contains("Properties", CompoundTag.TAG_LIST) ? "Properties"
: (tag.contains("properties", CompoundTag.TAG_LIST) ? "properties" : null);
if (listKey != null) {
ListTag propertiesList = tag.getList(listKey, CompoundTag.TAG_COMPOUND);
for (int i = 0; i < propertiesList.size(); i++) {
CompoundTag propTag = propertiesList.getCompound(i);
String propName = propTag.getString("name");
String value = propTag.getString("value");
String signature = propTag.contains("signature") ? propTag.getString("signature") : null;
if (signature != null) {
profile.getProperties().put(propName, new Property(propName, value, signature));
} else {
profile.getProperties().put(propName, new Property(propName, value));
}
}
return profile;
}
}
// COMPOUND 格式处理
if (propertiesKey != null && tag.contains(propertiesKey, CompoundTag.TAG_COMPOUND)) {
CompoundTag propertiesTag = tag.getCompound(propertiesKey);
for (String key : propertiesTag.getAllKeys()) { for (String key : propertiesTag.getAllKeys()) {
ListTag listTag = propertiesTag.getList(key, CompoundTag.TAG_COMPOUND); ListTag listTag = propertiesTag.getList(key, CompoundTag.TAG_COMPOUND);
for (int i = 0; i < listTag.size(); i++) { for (int i = 0; i < listTag.size(); i++) {
CompoundTag propTag = listTag.getCompound(i); CompoundTag propTag = listTag.getCompound(i);
String value = propTag.getString("Value"); String value = propTag.getString("Value");
if (propTag.contains("Signature", CompoundTag.TAG_STRING)) { String signature = propTag.contains("Signature") ? propTag.getString("Signature") : null;
profile.getProperties().put(key, new Property(key, value, propTag.getString("Signature"))); if (signature != null) {
profile.getProperties().put(key, new Property(key, value, signature));
} else { } else {
profile.getProperties().put(key, new Property(key, value)); profile.getProperties().put(key, new Property(key, value));
} }
} }
} }
} }
return profile; return profile;
} catch (Throwable e) { } catch (Throwable e) {
return null; return null;

View File

@ -16,9 +16,7 @@ void main() {
// 确保内外半径合理 // 确保内外半径合理
if (OuterRadius <= InnerRadius) { if (OuterRadius <= InnerRadius) {
vec4 color = vertexColor; discard; // ✅ 直接丢弃片段,不需要设置颜色
color.a = 0;
fragColor = color;
} }
// 计算环形 alpha // 计算环形 alpha
@ -28,20 +26,20 @@ void main() {
// 内边缘抗锯齿 // 内边缘抗锯齿
if (dist < InnerRadius + AntiAliasing) { if (dist < InnerRadius + AntiAliasing) {
float fade = (dist - InnerRadius) / AntiAliasing; float fade = (dist - InnerRadius) / AntiAliasing;
alpha *= fade; alpha *= clamp(fade, 0.0, 1.0); // ✅ 确保值在 [0,1] 范围内
} }
// 外边缘抗锯齿 // 外边缘抗锯齿
if (dist > OuterRadius - AntiAliasing) { if (dist > OuterRadius - AntiAliasing) {
float fade = 1.0 - (dist - (OuterRadius - AntiAliasing)) / AntiAliasing; float fade = 1.0 - (dist - (OuterRadius - AntiAliasing)) / AntiAliasing;
alpha *= fade; alpha *= clamp(fade, 0.0, 1.0); // ✅ 确保值在 [0,1] 范围内
} }
} }
vec4 color = vertexColor; vec4 color = vertexColor;
color.a *= alpha; color.a = color.a * alpha; // ✅ 修正语法
if (alpha > 0.0) { if (color.a > 0.0) {
fragColor = color * ColorModulator; fragColor = color * ColorModulator;
} else { } else {
discard; discard;

View File

@ -6,7 +6,6 @@ uniform vec4 ColorModulator;
uniform vec2 FramebufferSize; uniform vec2 FramebufferSize;
uniform vec2 Center; uniform vec2 Center;
uniform float Radius; uniform float Radius;
uniform float AntiAliasingRadius;
out vec4 fragColor; out vec4 fragColor;

View File

@ -32,14 +32,6 @@
"values": [ "values": [
0.0 0.0
] ]
},
{
"name": "AntiAliasingRadius",
"type": "float",
"count": 1,
"values": [
1.5
]
} }
] ]
} }

View File

@ -6,6 +6,7 @@
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "JAVA_21",
"mixins": [ "mixins": [
"carryon.MixinCarriedObjectRender", "carryon.MixinCarriedObjectRender",
"minecraft.MixinEntity",
"minecraft.CreativeModeTabsAccessor" "minecraft.CreativeModeTabsAccessor"
], ],
"client": [ "client": [

View File

@ -23,10 +23,14 @@ public class FabricDollComponentProvider implements IBlockComponentProvider {
@Override @Override
public void appendTooltip(ITooltip iTooltip, @NotNull BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { public void appendTooltip(ITooltip iTooltip, @NotNull BlockAccessor blockAccessor, IPluginConfig iPluginConfig) {
if (blockAccessor.getBlockEntity() instanceof DollBlockEntity doll) { if (blockAccessor.getBlockEntity() instanceof DollBlockEntity doll) {
GameProfile ownerProfile = doll.getOwnerProfile(); GameProfile ownerProfile;
if (ownerProfile != null) { if (doll.getOwnerProfile() != null) {
iTooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", ownerProfile.getName())); ownerProfile = doll.getOwnerProfile().gameProfile();
if (ownerProfile != null) {
iTooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", ownerProfile.getName()));
}
} }
} }
} }
} }

View File

@ -14,6 +14,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTab;
@ -25,10 +26,13 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.entity.SkullBlockEntity;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.api.callback.ActionResult;
import top.r3944realms.lib39.api.callback.AnvilUpdateCallback; import top.r3944realms.lib39.api.callback.AnvilUpdateCallback;
import top.r3944realms.lib39.api.callback.MinecraftSetUpServiceCallback;
import top.r3944realms.lib39.api.callback.SyncManagerRegisterCallback; import top.r3944realms.lib39.api.callback.SyncManagerRegisterCallback;
import top.r3944realms.lib39.api.callback.client.ClientWorldCallback; import top.r3944realms.lib39.api.callback.client.ClientWorldCallback;
import top.r3944realms.lib39.base.command.Lib39HelpCommand; import top.r3944realms.lib39.base.command.Lib39HelpCommand;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.content.item.DollItem; import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.core.register.Lib39Items; import top.r3944realms.lib39.core.register.Lib39Items;
import top.r3944realms.lib39.core.sync.ISyncData; import top.r3944realms.lib39.core.sync.ISyncData;
@ -44,6 +48,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
@ -183,31 +188,35 @@ public class FabricCommonEventHandler {
for (Map.Entry<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> resourceKeyListEntry : tabToItemsMap.entrySet()) { for (Map.Entry<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> resourceKeyListEntry : tabToItemsMap.entrySet()) {
ItemGroupEvents.modifyEntriesEvent(resourceKeyListEntry.getKey()).register(content -> resourceKeyListEntry.getValue().forEach(i -> content.accept(i.get()))); ItemGroupEvents.modifyEntriesEvent(resourceKeyListEntry.getKey()).register(content -> resourceKeyListEntry.getValue().forEach(i -> content.accept(i.get())));
} }
MinecraftSetUpServiceCallback.EVENT.register((services, mainThreadExecutor) -> {
DollBlockEntity.setup(services, mainThreadExecutor);
return ActionResult.SUCCESS;
});
AnvilUpdateCallback.EVENT.register((left, right, outputSlot, name, baseCost, player) -> { AnvilUpdateCallback.EVENT.register((left, right, outputSlot, name, baseCost, player) -> {
if (left.getItem() instanceof DollItem && name != null && name.length() < 15) { if (left.getItem() instanceof DollItem && name != null && !name.isEmpty() && name.length() <= 16) {
// 创建 GameProfile使用 NIL_UUID 临时占位 ItemStack output = Lib39Items.DOLL.get().getDefaultInstance();
output.setCount(left.getCount());
// 创建 ResolvableProfile
GameProfile profile = new GameProfile(Util.NIL_UUID, name); GameProfile profile = new GameProfile(Util.NIL_UUID, name);
ItemStack copied = Lib39Items.DOLL.get().getDefaultInstance();
copied.setCount(left.getCount());
// 使用 ResolvableProfile 进行异步解析
ResolvableProfile resolvableProfile = new ResolvableProfile(profile); ResolvableProfile resolvableProfile = new ResolvableProfile(profile);
if (!resolvableProfile.isResolved()) { output.set(DataComponents.PROFILE, resolvableProfile);
resolvableProfile.resolve().thenAcceptAsync(resolved -> {
// 解析完成后保存到 ItemStack
GameProfile resolvedProfile = resolved.gameProfile();
GameProfileHelper.saveProfileToItemStack(copied, resolvedProfile);
}, player.getServer());
} else {
GameProfileHelper.saveProfileToItemStack(copied, profile);
}
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(copied, 1, 1); // 使用 DollBlockEntity 的缓存异步获取完整档案
DollBlockEntity.fetchGameProfile(name).thenAcceptAsync(optional -> {
optional.ifPresent(fullProfile -> {
output.set(DataComponents.PROFILE, new ResolvableProfile(fullProfile));
});
}, SkullBlockEntity.CHECKED_MAIN_THREAD_EXECUTOR);
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(output, 1, 1);
} else if (name == null || name.isEmpty()) {
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(ItemStack.EMPTY, 0, 0);
} else { } else {
ItemStack defaultInstance = Items.BARRIER.getDefaultInstance(); ItemStack errorOutput = Items.BARRIER.getDefaultInstance();
defaultInstance.set(DataComponents.CUSTOM_NAME, errorOutput.set(DataComponents.CUSTOM_NAME,
Component.translatable("invalid.player_name.too_long")); Component.translatable("invalid.player_name.too_long"));
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(defaultInstance, 0, 0); return AnvilUpdateCallback.AnvilUpdateResult.withOutput(errorOutput, 0, 0);
} }
}); });
EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class) EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class)

View File

@ -290,8 +290,8 @@ public class FabricTestSyncData extends AbstractedTestSyncData implements IFabri
} }
@Override @Override
public void deserializeNBT(CompoundTag nbt) { public void deserializeNBT(CompoundTag t) {
NBTReader.of(nbt) NBTReader.of(t)
.intValue(NBT_KEY_INT, integer -> testInt = integer) .intValue(NBT_KEY_INT, integer -> testInt = integer)
.string(NBT_KEY_STRING, string -> testString = string) .string(NBT_KEY_STRING, string -> testString = string)
.booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool) .booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool)

View File

@ -1,5 +1,6 @@
package top.r3944realms.lib39.example.core.network; package top.r3944realms.lib39.example.core.network;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
/** /**
@ -10,6 +11,10 @@ public class FabricExNetworkHandler {
* 注册服务器接收的数据包 * 注册服务器接收的数据包
*/ */
public static void registerServerReceivers() { public static void registerServerReceivers() {
PayloadTypeRegistry.playC2S().register(
FabricClientDataPacket.TYPE,
FabricClientDataPacket.STREAM_CODEC
);
ServerPlayNetworking.registerGlobalReceiver( ServerPlayNetworking.registerGlobalReceiver(
FabricClientDataPacket.TYPE, FabricClientDataPacket.TYPE,
FabricClientDataPacket::handle FabricClientDataPacket::handle

View File

@ -1,5 +1,7 @@
package top.r3944realms.lib39.platform; package top.r3944realms.lib39.platform;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import top.r3944realms.lib39.platform.services.IUtilHelper; import top.r3944realms.lib39.platform.services.IUtilHelper;
import top.r3944realms.lib39.util.FabricBlockRegistryBuilder; import top.r3944realms.lib39.util.FabricBlockRegistryBuilder;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder; import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
@ -16,4 +18,9 @@ public enum FabricUtilHelper implements IUtilHelper {
public BlockRegistryBuilder getBlockRegistryBuilder() { public BlockRegistryBuilder getBlockRegistryBuilder() {
return new FabricBlockRegistryBuilder(); return new FabricBlockRegistryBuilder();
} }
@Override
public SyncData2Manager<?> getSyncData2Manager() {
return FabricCommonEventHandler.getSyncData2Manager();
}
} }

View File

@ -6,7 +6,6 @@
"compatibilityLevel": "JAVA_18", "compatibilityLevel": "JAVA_18",
"mixins": [ "mixins": [
"MixinApiLookUpImpl", "MixinApiLookUpImpl",
"MixinEntity",
"callback.MixinAnvilMenu" "callback.MixinAnvilMenu"
], ],
"client": [ "client": [

View File

@ -65,13 +65,12 @@ sourceSets.main.resources.srcDir project(':common').file('src/generated/resource
dependencies { dependencies {
compileOnly project(":common") compileOnly project(":common")
annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor")
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0")) implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0"))
implementation(group: 'tschipp.carryon', name: 'carryon-neoforge-1.21.1', version: '2.2.4.4') { implementation(group: 'tschipp.carryon', name: 'carryon-neoforge-1.21.1', version: '2.2.4.4') {
transitive = false transitive = false
} }
implementation "curse.maven:jade-324717:7545219" implementation "curse.maven:jade-324717:7545219"
implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.2.0"))
} }
// sourceJar任务 // sourceJar任务

View File

@ -24,9 +24,12 @@ public class NeoForgeDollComponentProvider implements IBlockComponentProvider {
@Override @Override
public void appendTooltip(ITooltip iTooltip, @NotNull BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { public void appendTooltip(ITooltip iTooltip, @NotNull BlockAccessor blockAccessor, IPluginConfig iPluginConfig) {
if (blockAccessor.getBlockEntity() instanceof DollBlockEntity doll) { if (blockAccessor.getBlockEntity() instanceof DollBlockEntity doll) {
GameProfile ownerProfile = doll.getOwnerProfile(); GameProfile ownerProfile;
if (ownerProfile != null) { if (doll.getOwnerProfile() != null) {
iTooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", ownerProfile.getName())); ownerProfile = doll.getOwnerProfile().gameProfile();
if (ownerProfile != null) {
iTooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", ownerProfile.getName()));
}
} }
} }
} }

View File

@ -13,6 +13,8 @@ import top.r3944realms.lib39.datagen.provider.SimpleLootTableProvider;
import top.r3944realms.lib39.datagen.provider.SubProvidersWrapper; import top.r3944realms.lib39.datagen.provider.SubProvidersWrapper;
import top.r3944realms.lib39.datagen.value.McLocale; import top.r3944realms.lib39.datagen.value.McLocale;
import java.util.concurrent.ExecutionException;
/** /**
* The type Lib 39 base data gen event. * The type Lib 39 base data gen event.
*/ */
@ -73,7 +75,14 @@ public class Lib39BaseDataGenEvent {
private static void LootTableDataGenerate(@NotNull GatherDataEvent event) { private static void LootTableDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider( event.getGenerator().addProvider(
event.includeServer(), event.includeServer(),
(DataProvider.Factory<SimpleLootTableProvider>) pOutput -> new SimpleLootTableProvider(pOutput, new SubProvidersWrapper().addBlockEntry(new Lib39BlockLootTable()), event.getLookupProvider()) (DataProvider.Factory<SimpleLootTableProvider>) pOutput -> {
try {
return new SimpleLootTableProvider(pOutput, new SubProvidersWrapper().addBlockEntry(new Lib39BlockLootTable(event.getLookupProvider())), event.getLookupProvider());
} catch (Exception e) {
logger.error("Failed to generate loot_table",e);
throw new RuntimeException(e);
}
}
); );
} }
private static void RecipeGenerator(@NotNull GatherDataEvent event) { private static void RecipeGenerator(@NotNull GatherDataEvent event) {

View File

@ -1,9 +1,13 @@
package top.r3944realms.lib39.base.datagen.provider; package top.r3944realms.lib39.base.datagen.provider;
import net.minecraft.core.HolderLookup;
import top.r3944realms.lib39.core.register.NeoForgeLib39Blocks; import top.r3944realms.lib39.core.register.NeoForgeLib39Blocks;
import top.r3944realms.lib39.core.register.Lib39Blocks; import top.r3944realms.lib39.core.register.Lib39Blocks;
import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables; import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/** /**
* The type Lib 39 block loot table. * The type Lib 39 block loot table.
*/ */
@ -11,8 +15,8 @@ public class Lib39BlockLootTable extends BlockLootTables {
/** /**
* Instantiates a new Lib 39 block loot table. * Instantiates a new Lib 39 block loot table.
*/ */
public Lib39BlockLootTable() { public Lib39BlockLootTable(CompletableFuture<HolderLookup.Provider> registries) throws ExecutionException, InterruptedException {
super(NeoForgeLib39Blocks.BLOCKS); super(NeoForgeLib39Blocks.BLOCKS, registries.get());
dropSelf(Lib39Blocks.DOLL, Lib39Blocks.WALL_DOLL); dropSelf(Lib39Blocks.DOLL, Lib39Blocks.WALL_DOLL);
} }
} }

View File

@ -9,7 +9,6 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
@ -17,6 +16,7 @@ import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
@ -30,10 +30,13 @@ import net.neoforged.neoforge.event.entity.EntityLeaveLevelEvent;
import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.neoforge.event.tick.ServerTickEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.api.event.MinecraftSetUpServiceEvent;
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent; import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
import top.r3944realms.lib39.base.command.Lib39HelpCommand; import top.r3944realms.lib39.base.command.Lib39HelpCommand;
import top.r3944realms.lib39.base.datagen.Lib39BaseDataGenEvent; import top.r3944realms.lib39.base.datagen.Lib39BaseDataGenEvent;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.content.item.DollItem; import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.core.network.NetworkHandler; import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.core.register.Lib39Items; import top.r3944realms.lib39.core.register.Lib39Items;
@ -153,31 +156,45 @@ public class CommonEventHandler {
public static void onAnvilRename(AnvilUpdateEvent event) { public static void onAnvilRename(AnvilUpdateEvent event) {
if (event.getLeft().getItem() instanceof DollItem) { if (event.getLeft().getItem() instanceof DollItem) {
String name = event.getName(); String name = event.getName();
Player player = event.getPlayer();
if (name != null && name.length() < 15) { if (name != null && !name.isEmpty()) {
GameProfile profile = new GameProfile(Util.NIL_UUID, name); if (name.length() <= 16) {
ItemStack copied = Lib39Items.DOLL.get().getDefaultInstance(); ItemStack output = Lib39Items.DOLL.get().getDefaultInstance();
ResolvableProfile resolvableProfile = new ResolvableProfile(profile); output.setCount(event.getLeft().getCount());
if (!resolvableProfile.isResolved()) {
resolvableProfile.resolve().thenAcceptAsync(resolved -> { // 创建 ResolvableProfile
// 解析完成后保存到 ItemStack GameProfile profile = new GameProfile(Util.NIL_UUID, name);
GameProfile resolvedProfile = resolved.gameProfile(); ResolvableProfile resolvableProfile = new ResolvableProfile(profile);
GameProfileHelper.saveProfileToItemStack(copied, resolvedProfile); output.set(DataComponents.PROFILE, resolvableProfile);
}, player.getServer());
// 使用 DollBlockEntity 的缓存异步获取完整档案
DollBlockEntity.fetchGameProfile(name).thenAcceptAsync(optional -> {
optional.ifPresent(fullProfile -> {
// 更新为完整的 ResolvableProfile
output.set(DataComponents.PROFILE, new ResolvableProfile(fullProfile));
});
}, SkullBlockEntity.CHECKED_MAIN_THREAD_EXECUTOR);
event.setOutput(output);
event.setCost(1);
event.setMaterialCost(1);
} else { } else {
GameProfileHelper.saveProfileToItemStack(copied, profile); ItemStack errorOutput = Items.BARRIER.getDefaultInstance();
errorOutput.set(DataComponents.CUSTOM_NAME,
Component.translatable("invalid.player_name.too_long"));
event.setOutput(errorOutput);
event.setCost(0);
} }
copied.setCount(event.getLeft().getCount());
event.setOutput(copied);
event.setCost(1);
} else { } else {
ItemStack defaultInstance = Items.BARRIER.getDefaultInstance(); event.setOutput(ItemStack.EMPTY);
defaultInstance.set(DataComponents.CUSTOM_NAME, event.setCost(0);
Component.translatable("invalid.player_name.too_long"));
event.setOutput(defaultInstance);
} }
} }
} }
@SubscribeEvent
public static void onMinecraftSetUpService (@NotNull MinecraftSetUpServiceEvent event) {
DollBlockEntity.setup(event.services, event.mainThreadExecutor);
}
/** /**
* On entity join world. * On entity join world.

View File

@ -0,0 +1,54 @@
package top.r3944realms.lib39.core.sync;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.neoforged.neoforge.capabilities.ICapabilityProvider;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.platform.Services;
/**
* NeoForge 版本的能力提供者
*
* @param <T> 同步数据类型必须继承 NBTEntitySyncData
*/
public abstract class SyncCapProvider<T extends NBTEntitySyncData> implements ICapabilityProvider<Entity, Void, T> {
/**
* 创建空的能力实例
* 当管理器中不存在数据时调用
*
* @param entity 实体
* @return 空的能力实例
*/
protected abstract T createEmptyCapability(Entity entity);
/**
* 获取能力对应的 ID
*
* @return 能力 ID
*/
protected abstract ResourceLocation getId();
@SuppressWarnings("unchecked")
@Override
public @Nullable T getCapability(Entity holder, Void context) {
// 从管理器中获取对应的管理器
return Services.PLATFORM.getUtilHelper().getSyncData2Manager().getManager(getId())
.map(manager -> {
// 从管理器的同步映射中获取数据
ISyncData<?> syncData = manager.getSyncMap().get(holder.getUUID());
if (syncData instanceof NBTEntitySyncData nbtSyncData) {
return (T) nbtSyncData;
}
// 不存在则创建空实例
T defaultCap = createEmptyCapability(holder);
// 加载已有的同步数据如果有
if (holder instanceof ILib39SyncDataHolder syncHolder) {
syncHolder.loadSyncData(defaultCap);
}
// 注册到管理器
manager.getSyncMap().put(holder.getUUID(), defaultCap);
return defaultCap;
})
.orElseGet(() -> createEmptyCapability(holder));
}
}

View File

@ -27,22 +27,23 @@ public class ExCapabilityHandler {
* @param event the event * @param event the event
*/ */
public static void registerCapability(@NotNull RegisterCapabilitiesEvent event) { public static void registerCapability(@NotNull RegisterCapabilitiesEvent event) {
TestSyncCapProvider provider = new TestSyncCapProvider();
EntityCapabilityHelper.registerForEntityClass( EntityCapabilityHelper.registerForEntityClass(
event, event,
TEST_CAP, TEST_CAP,
(entity, context) -> entity.getCapability(TEST_CAP), provider,
LivingEntity.class LivingEntity.class
); );
EntityCapabilityHelper.registerForEntityClass( EntityCapabilityHelper.registerForEntityClass(
event, event,
TEST_CAP, TEST_CAP,
(entity, context) -> entity.getCapability(TEST_CAP), provider,
Boat.class Boat.class
); );
EntityCapabilityHelper.registerForEntityClass( EntityCapabilityHelper.registerForEntityClass(
event, event,
TEST_CAP, TEST_CAP,
(entity, context) -> entity.getCapability(TEST_CAP), provider,
Minecart.class Minecart.class
); );
} }

View File

@ -0,0 +1,20 @@
package top.r3944realms.lib39.example.content.data;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.sync.SyncCapProvider;
public class TestSyncCapProvider extends SyncCapProvider<AbstractedTestSyncData> {
@Override
protected TestSyncData createEmptyCapability(Entity entity) {
return new TestSyncData(entity.getId(), entity);
}
@Override
protected ResourceLocation getId() {
return Lib39.rl("test_data");
}
}

View File

@ -287,8 +287,8 @@ public class TestSyncData extends AbstractedTestSyncData implements INeoForgeUpd
} }
@Override @Override
public void deserializeNBT(CompoundTag nbt) { public void deserializeNBT(CompoundTag t) {
NBTReader.of(nbt) NBTReader.of(t)
.intValue(NBT_KEY_INT, integer -> testInt = integer) .intValue(NBT_KEY_INT, integer -> testInt = integer)
.string(NBT_KEY_STRING, string -> testString = string) .string(NBT_KEY_STRING, string -> testString = string)
.booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool) .booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool)

View File

@ -1,5 +1,9 @@
package top.r3944realms.lib39.platform; package top.r3944realms.lib39.platform;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.event.CommonEventHandler;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import top.r3944realms.lib39.platform.services.IUtilHelper; import top.r3944realms.lib39.platform.services.IUtilHelper;
import top.r3944realms.lib39.util.NeoForgeBlockRegistryBuilder; import top.r3944realms.lib39.util.NeoForgeBlockRegistryBuilder;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder; import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
@ -12,8 +16,14 @@ public enum NeoForgeUtilHelper implements IUtilHelper {
* Instance forge util helper. * Instance forge util helper.
*/ */
INSTANCE; INSTANCE;
@Contract(value = " -> new", pure = true)
@Override @Override
public BlockRegistryBuilder getBlockRegistryBuilder() { public @NotNull BlockRegistryBuilder getBlockRegistryBuilder() {
return new NeoForgeBlockRegistryBuilder(); return new NeoForgeBlockRegistryBuilder();
} }
@Override
public SyncData2Manager<?> getSyncData2Manager() {
return CommonEventHandler.Game.getSyncData2Manager();
}
} }