diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java index a13fdcb..9424e2e 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java @@ -6,18 +6,52 @@ import carpet.api.settings.RuleCategory; import static com.github.zly2006.carpetslsaddition.ServerMain.CARPET_ID; public class SLSCarpetSettings { - @Rule(categories = {CARPET_ID}) + public static final String PCA = "pca"; // 用于描述兼容PCA的规则 + public static final String NEED_CLIENT = "needClient"; // 需要客户端安装SLS-Addition或实现相关支持 + public static final String PROTOCOL = "protocol"; + + @Rule(categories = {CARPET_ID, RuleCategory.SURVIVAL}) public static boolean obtainableReinforcedDeepSlate = false; - @Rule(categories = {CARPET_ID}) + + @Rule(categories = {CARPET_ID, RuleCategory.OPTIMIZATION}) public static int skipTicksForJoblessVillager = 0; - @Rule(categories = {CARPET_ID}, options = {"-1", "24", "50"}) + + @Rule(categories = {CARPET_ID, RuleCategory.OPTIMIZATION}, options = {"-1", "24", "50"}) public static int maxVillagersInABlock = -1; + @Rule(categories = {CARPET_ID, RuleCategory.CREATIVE}) public static boolean creativeNoInfinitePickup = false; + @Rule(categories = {CARPET_ID}) public static boolean autoUrl = false; - @Rule(categories = {CARPET_ID}) + + @Rule(categories = {CARPET_ID, RuleCategory.OPTIMIZATION}) public static boolean noBatSpawning = false; - @Rule(categories = {CARPET_ID}) + + @Rule(categories = {CARPET_ID, RuleCategory.FEATURE}) public static boolean offlineFakePlayers = false; + + @Rule(categories = {CARPET_ID, PCA, PROTOCOL}) + public static boolean pcaSyncProtocol = false; + + @Rule(categories = {CARPET_ID, PCA, PROTOCOL}) + public static PCA_SYNC_PLAYER_ENTITY_OPTIONS pcaSyncPlayerEntity = PCA_SYNC_PLAYER_ENTITY_OPTIONS.OPS; + + @Rule(categories = {CARPET_ID, PCA, RuleCategory.FEATURE, NEED_CLIENT}) + public static boolean emptyShulkerBoxStack = false; + + @Rule(categories = {CARPET_ID, PCA, RuleCategory.FEATURE}) + public static boolean useDyeOnShulkerBox = false; + + @Rule(categories = {CARPET_ID, PCA, PROTOCOL}, strict = false, options = {"#none"}) + public static String xaeroWorldName = "#none"; + + @Rule(categories = {CARPET_ID, PCA, RuleCategory.FEATURE}) + public static boolean playerSit = false; + + + public enum PCA_SYNC_PLAYER_ENTITY_OPTIONS { + NOBODY, BOT, OPS, OPS_AND_SELF, EVERYONE + } + } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java index 801205c..f74b64d 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java @@ -2,7 +2,6 @@ package com.github.zly2006.carpetslsaddition; import carpet.CarpetExtension; import carpet.CarpetServer; -import carpet.api.settings.SettingsManager; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -24,6 +23,8 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.collection.DefaultedList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.InputStream; @@ -33,11 +34,17 @@ import java.util.Map; public class ServerMain implements ModInitializer, CarpetExtension { public static final String MOD_ID = "carpet-sls-addition"; public static final String MOD_NAME = "Carpet SLS Addition"; - public static final String CARPET_ID = "slsaddition"; - static final Gson GSON = new Gson(); public static final Version MOD_VERSION = FabricLoader.getInstance().getModContainer(MOD_ID).get().getMetadata().getVersion(); + + public static final String CARPET_ID = "SLS"; + + public static final Logger LOGGER = LogManager.getLogger(MOD_ID); + static final Gson GSON = new Gson(); + public static ServerMain INSTANCE; - MinecraftServer server; + public static MinecraftServer server; + + public static final boolean tisCarpetLoaded = FabricLoader.getInstance().isModLoaded("carpet-tis-addition"); public static final String ITEM_NAME = "item_name"; public static final String OBSIDIAN_PICKAXE = "obsidian_pickaxe"; @@ -49,7 +56,7 @@ public class ServerMain implements ModInitializer, CarpetExtension { @Override public void onServerLoaded(MinecraftServer server) { - this.server = server; + ServerMain.server = server; CarpetServer.settingsManager.parseSettingsClass(SLSCarpetSettings.class); ((SettingsManagerAccessor) CarpetServer.settingsManager).loadSettings(); if (false) { @@ -108,4 +115,8 @@ public class ServerMain implements ModInitializer, CarpetExtension { return 1; })); } + + public static Identifier createId(String path) { + return new Identifier(MOD_ID, path); + } } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinAbstractSignBlock.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinAbstractSignBlock.java similarity index 97% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinAbstractSignBlock.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinAbstractSignBlock.java index 9a45249..ac71a72 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinAbstractSignBlock.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinAbstractSignBlock.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.block; import net.minecraft.block.*; import net.minecraft.block.entity.BlockEntity; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinBlock.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinBlock.java similarity index 96% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinBlock.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinBlock.java index 9b755a3..08b3ba9 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinBlock.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/block/MixinBlock.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.block; import carpet.patches.EntityPlayerMPFake; import com.github.zly2006.carpetslsaddition.PlayerAccess; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFakePlayer.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java similarity index 89% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFakePlayer.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java index d4f98d6..37acedc 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFakePlayer.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinFakePlayer.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.carpet; import carpet.patches.EntityPlayerMPFake; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; @@ -12,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import java.util.Optional; import java.util.UUID; -@Mixin(value = EntityPlayerMPFake.class) +@Mixin(value = EntityPlayerMPFake.class, remap = false) public class MixinFakePlayer { @Redirect(method = "createFake", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/UserCache;findByName(Ljava/lang/String;)Ljava/util/Optional;")) private static Optional onCreate(UserCache instance, String name) { diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSettingsManager.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinSettingsManager.java similarity index 86% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSettingsManager.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinSettingsManager.java index df66639..b4d541b 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSettingsManager.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/carpet/MixinSettingsManager.java @@ -1,12 +1,10 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.carpet; import carpet.api.settings.SettingsManager; import com.github.zly2006.carpetslsaddition.SettingsManagerAccessor; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import java.nio.file.Path; - @Mixin(value = SettingsManager.class, remap = false) public abstract class MixinSettingsManager implements SettingsManagerAccessor { @Shadow protected abstract void loadConfigurationFromConf(); diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinArmorStandEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinArmorStandEntity.java new file mode 100644 index 0000000..fdd19c1 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinArmorStandEntity.java @@ -0,0 +1,62 @@ +package com.github.zly2006.carpetslsaddition.mixin.entity; + +import com.github.zly2006.carpetslsaddition.util.SitEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ArmorStandEntity.class) +public abstract class MixinArmorStandEntity extends LivingEntity implements SitEntity { + private boolean sitEntity = false; // 用于标识该盔甲架是否用于玩家乘坐 + + protected MixinArmorStandEntity(EntityType entityType, World world) { + super(entityType, world); + } + + @Shadow + protected abstract void setMarker(boolean marker); + + @Override + public boolean isSitEntity() { + return sitEntity; + } + + @Override + public void setSitEntity(boolean isSitEntity) { + this.sitEntity = isSitEntity; + this.setMarker(isSitEntity); + this.setInvisible(isSitEntity); + } + + @Override + protected void removePassenger(Entity passenger) { + if (this.isSitEntity()) { + this.setPosition(this.getX(), this.getY() + 0.16, this.getZ()); + this.kill(); + } + super.removePassenger(passenger); + } + + @Inject(method = "writeCustomDataToNbt", at = @At(value = "RETURN")) + private void postWriteCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { + if (this.sitEntity) { + nbt.putBoolean("SitEntity", true); + } + } + + @Inject(method = "readCustomDataFromNbt", at = @At(value = "RETURN")) + private void postReadCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { + if (nbt.contains("SitEntity", NbtElement.BYTE_TYPE)) { + this.sitEntity = nbt.getBoolean("SitEntity"); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFallingBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java similarity index 94% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFallingBlockEntity.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java index fcc586f..f1cfa24 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinFallingBlockEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinFallingBlockEntity.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.entity; import net.minecraft.entity.FallingBlockEntity; import net.minecraft.entity.damage.DamageSource; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItemEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinItemEntity.java similarity index 95% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItemEntity.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinItemEntity.java index b8004ed..2f1c31f 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItemEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinItemEntity.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.entity; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import net.minecraft.entity.ItemEntity; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java similarity index 96% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerEntity.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java index 015986c..dfdd492 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinPlayerEntity.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.entity; import com.github.zly2006.carpetslsaddition.PlayerAccess; import net.minecraft.block.BlockState; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinServerPlayerEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinServerPlayerEntity.java new file mode 100644 index 0000000..3bf038a --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinServerPlayerEntity.java @@ -0,0 +1,66 @@ +package com.github.zly2006.carpetslsaddition.mixin.entity; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.SitEntity; +import com.mojang.authlib.GameProfile; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.encryption.PlayerPublicKey; +import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerPlayerEntity.class) +public abstract class MixinServerPlayerEntity extends PlayerEntity { + @Shadow + public ServerPlayNetworkHandler networkHandler; + + private int sneakTimes = 0; + private long lastSneakTime = 0; + + public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile gameProfile, @Nullable PlayerPublicKey publicKey) { + super(world, pos, yaw, gameProfile, publicKey); + } + + @Override + public void setSneaking(boolean sneaking){ + if (!SLSCarpetSettings.playerSit || (sneaking && this.isSneaking())) { + super.setSneaking(sneaking); + return; + } + + if (sneaking) { + long nowTime = System.currentTimeMillis(); + if (nowTime - lastSneakTime < 400 && sneakTimes == 0) { + return; + } + super.setSneaking(true); + if (this.isOnGround() && nowTime - lastSneakTime < 400) { + sneakTimes += 1; + if (sneakTimes == 3) { + ArmorStandEntity armorStandEntity = new ArmorStandEntity(world, this.getX(), this.getY() - 0.16, this.getZ()); + ((SitEntity) armorStandEntity).setSitEntity(true); + world.spawnEntity(armorStandEntity); + this.setSneaking(false); + this.startRiding(armorStandEntity); + sneakTimes = 0; + } + } else { + sneakTimes = 1; + } + lastSneakTime = nowTime; + } else { + super.setSneaking(false); + // 同步潜行状态到客户端 + // 如果不同步的话客户端会认为仍在潜行,从而碰撞箱的高度会计算错误 + if (sneakTimes == 0 && this.networkHandler != null) { + this.networkHandler.sendPacket(new EntityTrackerUpdateS2CPacket(this.getId(), this.getDataTracker(), true)); + } + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinVillagerEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinVillagerEntity.java similarity index 96% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinVillagerEntity.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinVillagerEntity.java index 24d78fe..28da752 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinVillagerEntity.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/entity/MixinVillagerEntity.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.entity; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import net.minecraft.entity.EntityType; @@ -37,7 +37,7 @@ public abstract class MixinVillagerEntity extends MerchantEntity { tickCount--; } else { - tickCount = SLSCarpetSettings.skipTicksForJoblessVillager + 1; + tickCount = SLSCarpetSettings.skipTicksForJoblessVillager; ci.cancel(); if (this.getHeadRollingTimeLeft() > 0) { diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinDyeItem.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinDyeItem.java new file mode 100644 index 0000000..c50bf3b --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinDyeItem.java @@ -0,0 +1,55 @@ +package com.github.zly2006.carpetslsaddition.mixin.item; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.item.DyeItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.util.ActionResult; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(DyeItem.class) +public abstract class MixinDyeItem extends Item { + public MixinDyeItem(Settings settings) { + super(settings); + } + + @Shadow + public abstract DyeColor getColor(); + + @Override + public ActionResult useOnBlock(ItemUsageContext context) { + if (!SLSCarpetSettings.useDyeOnShulkerBox) { + return ActionResult.PASS; + } + World world = context.getWorld(); + BlockPos pos = context.getBlockPos(); + BlockState blockState = world.getBlockState(pos); + + if (blockState.isOf(Blocks.SHULKER_BOX)) { + if (!world.isClient()) { + ShulkerBoxBlockEntity blockEntity = (ShulkerBoxBlockEntity) world.getBlockEntity(pos); + BlockState newBlockState = ShulkerBoxBlock.get(getColor()).getDefaultState().with(ShulkerBoxBlock.FACING, blockState.get(ShulkerBoxBlock.FACING)); + + if (world.setBlockState(pos, newBlockState)) { + ShulkerBoxBlockEntity newBlockEntity = (ShulkerBoxBlockEntity) world.getBlockEntity(pos); + assert blockEntity != null; + assert newBlockEntity != null; + newBlockEntity.readInventoryNbt(blockEntity.createNbt()); + newBlockEntity.setCustomName(blockEntity.getCustomName()); + newBlockEntity.markDirty(); + context.getStack().decrement(1); + } + } + return ActionResult.success(world.isClient); + } + return ActionResult.PASS; + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItem.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinItem.java similarity index 94% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItem.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinItem.java index ff012eb..37972c0 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinItem.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinItem.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.item; import com.github.zly2006.carpetslsaddition.PlayerAccess; import net.minecraft.block.BlockState; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinPotionItem.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinPotionItem.java new file mode 100644 index 0000000..99300ef --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/item/MixinPotionItem.java @@ -0,0 +1,68 @@ +package com.github.zly2006.carpetslsaddition.mixin.item; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.*; +import net.minecraft.potion.PotionUtil; +import net.minecraft.potion.Potions; +import net.minecraft.util.ActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Objects; + +@Mixin(PotionItem.class) +public abstract class MixinPotionItem extends Item { + public MixinPotionItem(Settings settings) { + super(settings); + } + + @Inject(method = "useOnBlock", at = @At(value = "HEAD"), cancellable = true) + public void useOnBlock(ItemUsageContext context, CallbackInfoReturnable cir) { + ItemStack itemStack = context.getStack(); + PlayerEntity player = context.getPlayer(); + + if (!SLSCarpetSettings.useDyeOnShulkerBox || + player == null || + itemStack.getItem() != Items.POTION || + PotionUtil.getPotion(itemStack) != Potions.WATER) { + return; + } + + World world = context.getWorld(); + BlockPos pos = context.getBlockPos(); + BlockState blockState = world.getBlockState(pos); + Block block = blockState.getBlock(); + + if (block instanceof ShulkerBoxBlock && ((ShulkerBoxBlock) block).getColor() != null) { + if (!world.isClient()) { + ShulkerBoxBlockEntity blockEntity = (ShulkerBoxBlockEntity) world.getBlockEntity(pos); + BlockState newBlockState = Blocks.SHULKER_BOX.getDefaultState(). + with(ShulkerBoxBlock.FACING, blockState.get(ShulkerBoxBlock.FACING)); + + if (world.setBlockState(pos, newBlockState)) { + ShulkerBoxBlockEntity newBlockEntity = (ShulkerBoxBlockEntity) world.getBlockEntity(pos); + assert blockEntity != null; + assert newBlockEntity != null; + newBlockEntity.readInventoryNbt(blockEntity.createNbt()); + newBlockEntity.setCustomName(blockEntity.getCustomName()); + newBlockEntity.markDirty(); + if (!player.isCreative()) { + context.getStack().decrement(1); + Objects.requireNonNull(context.getPlayer()).getInventory().insertStack(new ItemStack(Items.GLASS_BOTTLE)); + } + } + } + cir.setReturnValue(ActionResult.success(world.isClient)); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/AccessorPlayerAbilities.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/AccessorPlayerAbilities.java similarity index 95% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/AccessorPlayerAbilities.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/AccessorPlayerAbilities.java index 57f67bf..1bfd3c0 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/AccessorPlayerAbilities.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/AccessorPlayerAbilities.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.player; import net.minecraft.entity.player.PlayerAbilities; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerInventory.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/MixinPlayerInventory.java similarity index 50% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerInventory.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/MixinPlayerInventory.java index 133e049..d090a8a 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinPlayerInventory.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/player/MixinPlayerInventory.java @@ -1,20 +1,19 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.player; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import net.minecraft.entity.player.PlayerAbilities; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import org.spongepowered.asm.mixin.Final; +import net.minecraft.util.Nameable; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(PlayerInventory.class) -public class MixinPlayerInventory { +public abstract class MixinPlayerInventory implements Inventory, Nameable { @Redirect(method = "insertStack(ILnet/minecraft/item/ItemStack;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getAbilities()Lnet/minecraft/entity/player/PlayerAbilities;")) private PlayerAbilities insertStack(PlayerEntity instance) { if (SLSCarpetSettings.creativeNoInfinitePickup) { @@ -32,4 +31,27 @@ public class MixinPlayerInventory { } return instance.getAbilities(); } + + // 修改潜影盒拾取逻辑,使从地上捡起潜影盒时仍可堆叠 + @Redirect(method = "canStackAddMore", + at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isStackable()Z", ordinal = 0)) + private boolean canStackAddMoreIsStackable(ItemStack itemStack) { + return ShulkerBoxItemUtil.isStackable(itemStack); + } + + @Redirect(method = "canStackAddMore", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", ordinal = 0)) + private int canStackAddMoreGetMaxCount(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } + + @Redirect(method = "addStack(ILnet/minecraft/item/ItemStack;)I", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", ordinal = -1)) + private int addStackGetMaxCount(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } + + // 避免死循环 + @Redirect(method = "offer", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", ordinal = 0)) + private int offerGetMaxCount(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinAbstractFurnaceBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinAbstractFurnaceBlockEntity.java new file mode 100644 index 0000000..64d2981 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinAbstractFurnaceBlockEntity.java @@ -0,0 +1,31 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.recipe.RecipeInputProvider; +import net.minecraft.recipe.RecipeUnlocker; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(AbstractFurnaceBlockEntity.class) +public abstract class MixinAbstractFurnaceBlockEntity extends LockableContainerBlockEntity implements SidedInventory, RecipeUnlocker, RecipeInputProvider { + + + protected MixinAbstractFurnaceBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update AbstractFurnaceBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBarrelBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBarrelBlockEntity.java new file mode 100644 index 0000000..c17279d --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBarrelBlockEntity.java @@ -0,0 +1,28 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BarrelBlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BarrelBlockEntity.class) +public abstract class MixinBarrelBlockEntity extends LootableContainerBlockEntity { + + protected MixinBarrelBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update BarrelBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBeehiveBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBeehiveBlockEntity.java new file mode 100644 index 0000000..18e6511 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBeehiveBlockEntity.java @@ -0,0 +1,59 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BeehiveBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.Objects; + +@Mixin(BeehiveBlockEntity.class) +public abstract class MixinBeehiveBlockEntity extends BlockEntity { + + + public MixinBeehiveBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Inject(method = "tickBees", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;remove()V", shift = At.Shift.AFTER)) + private static void postTickBees(World world, BlockPos pos, BlockState state, List bees, BlockPos flowerPos, CallbackInfo ci) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(Objects.requireNonNull(world.getBlockEntity(pos)))) { + ServerMain.LOGGER.debug("update BeehiveBlockEntity: {}", pos); + } + } + + @Inject(method = "tryReleaseBee", at = @At(value = "RETURN")) + public void postTryReleaseBee(BlockState state, BeehiveBlockEntity.BeeState beeState, CallbackInfoReturnable> cir) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this) && cir.getReturnValue() != null) { + ServerMain.LOGGER.debug("update BeehiveBlockEntity: {}", this.pos); + } + } + + @Inject(method = "readNbt", at = @At(value = "RETURN")) + public void postFromTag(NbtCompound nbt, CallbackInfo ci) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update BeehiveBlockEntity: {}", this.pos); + } + } + + @Inject(method = "tryEnterHive(Lnet/minecraft/entity/Entity;ZI)V", at = @At(value = "INVOKE", + target = "Lnet/minecraft/entity/Entity;discard()V", ordinal = 0)) + public void postEnterHive(Entity entity, boolean hasNectar, int ticksInHive, CallbackInfo ci) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update BeehiveBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBrewingStandBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBrewingStandBlockEntity.java new file mode 100644 index 0000000..b6e6005 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinBrewingStandBlockEntity.java @@ -0,0 +1,28 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.BrewingStandBlockEntity; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BrewingStandBlockEntity.class) +public abstract class MixinBrewingStandBlockEntity extends LockableContainerBlockEntity implements SidedInventory { + + protected MixinBrewingStandBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update BrewingStandBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinChestBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinChestBlockEntity.java new file mode 100644 index 0000000..f411a2c --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinChestBlockEntity.java @@ -0,0 +1,30 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.block.entity.LidOpenable; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +// 由于陷阱箱继承自箱子,因此不用 mixin 陷阱箱 +// implements ChestAnimationProgress 会出错 不知道为啥 +@Mixin(ChestBlockEntity.class) +public abstract class MixinChestBlockEntity extends LootableContainerBlockEntity implements LidOpenable { + + protected MixinChestBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update ChestBlockEntity: {}", this.pos); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinComparatorBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinComparatorBlockEntity.java new file mode 100644 index 0000000..5c00806 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinComparatorBlockEntity.java @@ -0,0 +1,26 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.ComparatorBlockEntity; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ComparatorBlockEntity.class) +public abstract class MixinComparatorBlockEntity extends BlockEntity { + public MixinComparatorBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update ComparatorBlockEntity: {}", this.pos); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinDispenserBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinDispenserBlockEntity.java new file mode 100644 index 0000000..109f0a6 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinDispenserBlockEntity.java @@ -0,0 +1,27 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.DispenserBlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(DispenserBlockEntity.class) +public abstract class MixinDispenserBlockEntity extends LootableContainerBlockEntity { + + protected MixinDispenserBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update DispenserBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinHopperBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinHopperBlockEntity.java new file mode 100644 index 0000000..bc87e98 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinHopperBlockEntity.java @@ -0,0 +1,41 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.Hopper; +import net.minecraft.block.entity.HopperBlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.BooleanSupplier; + +@Mixin(HopperBlockEntity.class) +public abstract class MixinHopperBlockEntity extends LootableContainerBlockEntity implements Hopper { + + protected MixinHopperBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Inject(method = "insertAndExtract", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/entity/HopperBlockEntity;markDirty(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V")) + private static void onInsertAndExtract(World world, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier booleanSupplier, CallbackInfoReturnable cir) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(blockEntity)) { + ServerMain.LOGGER.debug("update HopperBlockEntity: {}", pos); + } + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update HopperBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinShulkerBoxBlockEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinShulkerBoxBlockEntity.java new file mode 100644 index 0000000..03a8ffc --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/block/MixinShulkerBoxBlockEntity.java @@ -0,0 +1,28 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.block; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ShulkerBoxBlockEntity.class) +public abstract class MixinShulkerBoxBlockEntity extends LootableContainerBlockEntity implements SidedInventory { + + protected MixinShulkerBoxBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Override + public void markDirty() { + super.markDirty(); + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncBlockEntityToClient(this)) { + ServerMain.LOGGER.debug("update ShulkerBoxBlockEntity: {}", this.pos); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinHorseBaseEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinHorseBaseEntity.java new file mode 100644 index 0000000..4ed776b --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinHorseBaseEntity.java @@ -0,0 +1,31 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.entity; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.JumpingMount; +import net.minecraft.entity.Saddleable; +import net.minecraft.entity.passive.AbstractHorseEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.InventoryChangedListener; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AbstractHorseEntity.class) +public abstract class MixinHorseBaseEntity extends AnimalEntity implements InventoryChangedListener, JumpingMount, Saddleable { + protected MixinHorseBaseEntity(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "onInventoryChanged", at = @At(value = "HEAD")) + private void updateEntity(Inventory sender, CallbackInfo ci) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncEntityToClient(this)) { + ServerMain.LOGGER.debug("update HorseBaseEntity inventory: onInventoryChanged."); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinMerchantEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinMerchantEntity.java new file mode 100644 index 0000000..8b2abff --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinMerchantEntity.java @@ -0,0 +1,48 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.entity; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.Npc; +import net.minecraft.entity.passive.MerchantEntity; +import net.minecraft.entity.passive.PassiveEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.InventoryChangedListener; +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.village.Merchant; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + + +@Mixin(MerchantEntity.class) +public abstract class MixinMerchantEntity extends PassiveEntity implements Npc, Merchant, InventoryChangedListener { + @Final + @Shadow + private SimpleInventory inventory; + + protected MixinMerchantEntity(EntityType entityType, World world) { + super(entityType, world); + } + + + @Inject(method = "(Lnet/minecraft/entity/EntityType;Lnet/minecraft/world/World;)V", at = @At(value = "RETURN")) + private void addInventoryListener(EntityType entityType, World world, CallbackInfo info) { + if (this.world.isClient()) { + return; + } + this.inventory.addListener(this); + } + + @Override + public void onInventoryChanged(Inventory inventory) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncEntityToClient(this)) { + ServerMain.LOGGER.debug("update villager inventory: onInventoryChanged."); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinStorageMinecartEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinStorageMinecartEntity.java new file mode 100644 index 0000000..55d41f7 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/protocol/pcaSyncProtocol/entity/MixinStorageMinecartEntity.java @@ -0,0 +1,29 @@ +package com.github.zly2006.carpetslsaddition.mixin.protocol.pcaSyncProtocol.entity; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import com.github.zly2006.carpetslsaddition.network.pca.PcaSyncProtocol; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.vehicle.AbstractMinecartEntity; +import net.minecraft.entity.vehicle.StorageMinecartEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(StorageMinecartEntity.class) +public abstract class MixinStorageMinecartEntity extends AbstractMinecartEntity implements Inventory, NamedScreenHandlerFactory { + protected MixinStorageMinecartEntity(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "markDirty", at = @At(value = "RETURN")) + private void updateInventory(CallbackInfo ci) { + if (SLSCarpetSettings.pcaSyncProtocol && PcaSyncProtocol.syncEntityToClient(this)) { + ServerMain.LOGGER.debug("update StorageMinecartEntity inventory."); + } + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinScreenHandler.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinScreenHandler.java new file mode 100644 index 0000000..41fca25 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinScreenHandler.java @@ -0,0 +1,26 @@ +package com.github.zly2006.carpetslsaddition.mixin.screen; + +import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ScreenHandler.class) +public class MixinScreenHandler { + @Redirect(method = "insertItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isStackable()Z", ordinal = 0)) + private boolean insertItemIsStackable(ItemStack itemStack) { + return ShulkerBoxItemUtil.isStackable(itemStack); + } + + @Redirect(method = "insertItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I")) + private int insertItemGetMaxCount0(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } + + @Redirect(method = "internalOnSlotClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", ordinal = -1)) + private int removeStackGetMaxCount(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinSlot.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinSlot.java new file mode 100644 index 0000000..682e30e --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/screen/MixinSlot.java @@ -0,0 +1,16 @@ +package com.github.zly2006.carpetslsaddition.mixin.screen; + +import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Slot.class) +public class MixinSlot { + @Redirect(method = "getMaxItemCount(Lnet/minecraft/item/ItemStack;)I", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", ordinal = 0)) + private int getMaxItemCount(ItemStack itemStack) { + return ShulkerBoxItemUtil.getMaxCount(itemStack); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/server/MixinPlayerManager.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/server/MixinPlayerManager.java new file mode 100644 index 0000000..5fe4bbc --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/server/MixinPlayerManager.java @@ -0,0 +1,38 @@ +package com.github.zly2006.carpetslsaddition.mixin.server; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.zip.CRC32; + +@Mixin(PlayerManager.class) +public class MixinPlayerManager { + @Inject(method = "sendWorldInfo", at = @At(value = "RETURN")) + public void preOnSendWorldInfo(ServerPlayerEntity player, ServerWorld world, CallbackInfo ci) { + if (SLSCarpetSettings.xaeroWorldName.equals("#none")) { + return; + } + Identifier xaeroworldmap = new Identifier("xaeroworldmap", "main"); + Identifier xaerominimap = new Identifier("xaerominimap", "main"); + + CRC32 crc = new CRC32(); + crc.update(SLSCarpetSettings.xaeroWorldName.getBytes()); + ByteBuf buf = Unpooled.buffer(); + buf.writeByte(0); + buf.writeInt((int) crc.getValue()); + + ServerPlayNetworking.send(player, xaeroworldmap, new PacketByteBuf(buf.duplicate())); + ServerPlayNetworking.send(player, xaerominimap, new PacketByteBuf(buf.duplicate())); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSpawnHelper.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinSpawnHelper.java similarity index 93% rename from src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSpawnHelper.java rename to src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinSpawnHelper.java index 7ce4fdf..817bcb1 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/MixinSpawnHelper.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/world/MixinSpawnHelper.java @@ -1,4 +1,4 @@ -package com.github.zly2006.carpetslsaddition.mixin; +package com.github.zly2006.carpetslsaddition.mixin.world; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import net.minecraft.entity.EntityType; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/network/pca/PcaSyncProtocol.java b/src/main/java/com/github/zly2006/carpetslsaddition/network/pca/PcaSyncProtocol.java new file mode 100644 index 0000000..6e3d803 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/network/pca/PcaSyncProtocol.java @@ -0,0 +1,418 @@ +package com.github.zly2006.carpetslsaddition.network.pca; + +import carpet.CarpetServer; +import carpet.api.settings.CarpetRule; +import carpet.api.settings.RuleHelper; +import carpet.patches.EntityPlayerMPFake; +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import io.netty.buffer.Unpooled; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.block.BarrelBlock; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ChestBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.enums.ChestType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +public class PcaSyncProtocol { + + public static final ReentrantLock lock = new ReentrantLock(true); + public static final ReentrantLock pairLock = new ReentrantLock(true); + // 发送包 + private static final Identifier ENABLE_PCA_SYNC_PROTOCOL = ServerMain.createId("enable_pca_sync_protocol"); + private static final Identifier DISABLE_PCA_SYNC_PROTOCOL = ServerMain.createId("disable_pca_sync_protocol"); + private static final Identifier UPDATE_ENTITY = ServerMain.createId("update_entity"); + private static final Identifier UPDATE_BLOCK_ENTITY = ServerMain.createId("update_block_entity"); + // 响应包 + private static final Identifier SYNC_BLOCK_ENTITY = ServerMain.createId("sync_block_entity"); + private static final Identifier SYNC_ENTITY = ServerMain.createId("sync_entity"); + private static final Identifier CANCEL_SYNC_BLOCK_ENTITY = ServerMain.createId("cancel_sync_block_entity"); + private static final Identifier CANCEL_SYNC_ENTITY = ServerMain.createId("cancel_sync_entity"); + private static final Map> playerWatchBlockPos = new HashMap<>(); + private static final Map> playerWatchEntity = new HashMap<>(); + private static final Map, Set> blockPosWatchPlayerSet = new HashMap<>(); + private static final Map, Set> entityWatchPlayerSet = new HashMap<>(); + private static final MutablePair identifierEntityPair = new MutablePair<>(); + private static final MutablePair identifierBlockPosPair = new MutablePair<>(); + + // 通知客户端服务器已启用 PcaSyncProtocol + public static void enablePcaSyncProtocol(@NotNull ServerPlayerEntity player) { + // 在这写如果是在 BC 端的情况下,ServerPlayNetworking.canSend 在这个时机调用会出现错误 + ServerMain.LOGGER.debug("Try enablePcaSyncProtocol: {}", player.getName().getString()); + // bc 端比较奇怪,canSend 工作不正常 + // if (ServerPlayNetworking.canSend(player, ENABLE_PCA_SYNC_PROTOCOL)) { + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + ServerPlayNetworking.send(player, ENABLE_PCA_SYNC_PROTOCOL, buf); + ServerMain.LOGGER.debug("send enablePcaSyncProtocol to {}!", player.getName().getString()); + lock.lock(); + lock.unlock(); + } + + // 通知客户端服务器已停用 PcaSyncProtocol + public static void disablePcaSyncProtocol(@NotNull ServerPlayerEntity player) { + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + ServerPlayNetworking.send(player, DISABLE_PCA_SYNC_PROTOCOL, buf); + ServerMain.LOGGER.debug("send disablePcaSyncProtocol to {}!", player.getName().getString()); + } + + // 通知客户端更新 Entity + // 包内包含 World 的 Identifier, entityId, entity 的 nbt 数据 + // 传输 World 是为了通知客户端该 Entity 属于哪个 World + public static void updateEntity(@NotNull ServerPlayerEntity player, @NotNull Entity entity) { + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + buf.writeIdentifier(entity.getEntityWorld().getRegistryKey().getValue()); + buf.writeInt(entity.getId()); + buf.writeNbt(entity.writeNbt(new NbtCompound())); + ServerPlayNetworking.send(player, UPDATE_ENTITY, buf); + } + + // 通知客户端更新 BlockEntity + // 包内包含 World 的 Identifier, pos, blockEntity 的 nbt 数据 + // 传输 World 是为了通知客户端该 BlockEntity 属于哪个世界 + public static void updateBlockEntity(@NotNull ServerPlayerEntity player, @NotNull BlockEntity blockEntity) { + World world = blockEntity.getWorld(); + + // 在生成世界时可能会产生空指针 + if (world == null) { + return; + } + + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + buf.writeIdentifier(world.getRegistryKey().getValue()); + buf.writeBlockPos(blockEntity.getPos()); + buf.writeNbt(blockEntity.createNbt()); + ServerPlayNetworking.send(player, UPDATE_BLOCK_ENTITY, buf); + } + + public static void init() { + ServerPlayNetworking.registerGlobalReceiver(SYNC_BLOCK_ENTITY, PcaSyncProtocol::syncBlockEntityHandler); + ServerPlayNetworking.registerGlobalReceiver(SYNC_ENTITY, PcaSyncProtocol::syncEntityHandler); + ServerPlayNetworking.registerGlobalReceiver(CANCEL_SYNC_BLOCK_ENTITY, PcaSyncProtocol::cancelSyncBlockEntityHandler); + ServerPlayNetworking.registerGlobalReceiver(CANCEL_SYNC_ENTITY, PcaSyncProtocol::cancelSyncEntityHandler); + ServerPlayConnectionEvents.JOIN.register(PcaSyncProtocol::onJoin); + ServerPlayConnectionEvents.DISCONNECT.register(PcaSyncProtocol::onDisconnect); + } + + private static void onDisconnect(ServerPlayNetworkHandler serverPlayNetworkHandler, MinecraftServer minecraftServer) { + if (SLSCarpetSettings.pcaSyncProtocol) { + ServerMain.LOGGER.debug("onDisconnect remove: {}", serverPlayNetworkHandler.player.getName().getString()); + } + } + + private static void onJoin(ServerPlayNetworkHandler serverPlayNetworkHandler, PacketSender packetSender, MinecraftServer minecraftServer) { + if (SLSCarpetSettings.pcaSyncProtocol) { + enablePcaSyncProtocol(serverPlayNetworkHandler.player); + } + } + + // 客户端通知服务端取消 BlockEntity 同步 + private static void cancelSyncBlockEntityHandler(MinecraftServer server, ServerPlayerEntity player, + ServerPlayNetworkHandler handler, PacketByteBuf buf, + PacketSender responseSender) { + if (!SLSCarpetSettings.pcaSyncProtocol) { + return; + } + ServerMain.LOGGER.debug("{} cancel watch blockEntity.", player.getName().getString()); + PcaSyncProtocol.clearPlayerWatchBlock(player); + } + + // 客户端通知服务端取消 Entity 同步 + private static void cancelSyncEntityHandler(MinecraftServer server, ServerPlayerEntity player, + ServerPlayNetworkHandler handler, PacketByteBuf buf, + PacketSender responseSender) { + if (!SLSCarpetSettings.pcaSyncProtocol) { + return; + } + ServerMain.LOGGER.debug("{} cancel watch entity.", player.getName().getString()); + PcaSyncProtocol.clearPlayerWatchEntity(player); + } + + // 客户端请求同步 BlockEntity + // 包内包含 pos + // 由于正常的场景一般不会跨世界请求数据,因此包内并不包含 World,以玩家所在的 World 为准 + private static void syncBlockEntityHandler(MinecraftServer server, ServerPlayerEntity player, + ServerPlayNetworkHandler handler, PacketByteBuf buf, + PacketSender responseSender) { + if (!SLSCarpetSettings.pcaSyncProtocol) { + return; + } + BlockPos pos = buf.readBlockPos(); + ServerWorld world = player.getWorld(); + BlockState blockState = world.getBlockState(pos); + clearPlayerWatchData(player); + ServerMain.LOGGER.debug("{} watch blockpos {}: {}", player.getName().getString(), pos, blockState); + + BlockEntity blockEntityAdj = null; + // 不是单个箱子则需要更新隔壁箱子 + if (blockState.getBlock() instanceof ChestBlock) { + if (blockState.get(ChestBlock.CHEST_TYPE) != ChestType.SINGLE) { + BlockPos posAdj = pos.offset(ChestBlock.getFacing(blockState)); + // The method in World now checks that the caller is from the same thread... + blockEntityAdj = world.getWorldChunk(posAdj).getBlockEntity(posAdj); + } + } else if (ServerMain.tisCarpetLoaded && blockState.isOf(Blocks.BARREL) && RuleHelper.getBooleanValue(getCarpetRule("largeBarrel"))) { + Direction directionOpposite = blockState.get(BarrelBlock.FACING).getOpposite(); + BlockPos posAdj = pos.offset(directionOpposite); + BlockState blockStateAdj = world.getBlockState(posAdj); + if (blockStateAdj.isOf(Blocks.BARREL) && blockStateAdj.get(BarrelBlock.FACING) == directionOpposite) { + blockEntityAdj = world.getWorldChunk(posAdj).getBlockEntity(posAdj); + } + } + + if (blockEntityAdj != null) { + updateBlockEntity(player, blockEntityAdj); + } + + // 本来想判断一下 blockState 类型做个白名单的,考虑到 client 已经做了判断就不在服务端做判断了 + // 就算被恶意攻击应该不会造成什么损失 + // 大不了 op 直接拉黑 + // The method in World now checks that the caller is from the same thread... + BlockEntity blockEntity = world.getWorldChunk(pos).getBlockEntity(pos); + if (blockEntity != null) { + updateBlockEntity(player, blockEntity); + } + + Pair pair = new ImmutablePair<>(player.getEntityWorld().getRegistryKey().getValue(), pos); + lock.lock(); + playerWatchBlockPos.put(player, pair); + if (!blockPosWatchPlayerSet.containsKey(pair)) { + blockPosWatchPlayerSet.put(pair, new HashSet<>()); + } + blockPosWatchPlayerSet.get(pair).add(player); + lock.unlock(); + } + + // 客户端请求同步 Entity + // 包内包含 entityId + // 由于正常的场景一般不会跨世界请求数据,因此包内并不包含 World,以玩家所在的 World 为准 + private static void syncEntityHandler(MinecraftServer server, ServerPlayerEntity player, + ServerPlayNetworkHandler handler, PacketByteBuf buf, + PacketSender responseSender) { + if (!SLSCarpetSettings.pcaSyncProtocol) { + return; + } + int entityId = buf.readInt(); + ServerWorld world = player.getWorld(); + Entity entity = world.getEntityById(entityId); + if (entity == null) { + ServerMain.LOGGER.debug("Can't find entity {}.", entityId); + } else { + clearPlayerWatchData(player); + if (entity instanceof PlayerEntity) { + if (SLSCarpetSettings.pcaSyncPlayerEntity == SLSCarpetSettings.PCA_SYNC_PLAYER_ENTITY_OPTIONS.NOBODY) { + return; + } else if (SLSCarpetSettings.pcaSyncPlayerEntity == SLSCarpetSettings.PCA_SYNC_PLAYER_ENTITY_OPTIONS.BOT) { + if (!(entity instanceof EntityPlayerMPFake)) { + return; + } + } else if (SLSCarpetSettings.pcaSyncPlayerEntity == SLSCarpetSettings.PCA_SYNC_PLAYER_ENTITY_OPTIONS.OPS) { + if (!(entity instanceof EntityPlayerMPFake) && server.getPermissionLevel(player.getGameProfile()) < 2) { + return; + } + } else if (SLSCarpetSettings.pcaSyncPlayerEntity == SLSCarpetSettings.PCA_SYNC_PLAYER_ENTITY_OPTIONS.OPS_AND_SELF) { + if (!(entity instanceof EntityPlayerMPFake) && + server.getPermissionLevel(player.getGameProfile()) < 2 && + entity != player) { + return; + } + } else if (SLSCarpetSettings.pcaSyncPlayerEntity != SLSCarpetSettings.PCA_SYNC_PLAYER_ENTITY_OPTIONS.EVERYONE) { + // wtf???? + ServerMain.LOGGER.warn("syncEntityHandler wtf???"); + return; + } + } + ServerMain.LOGGER.debug("{} watch entity {}: {}", player.getName().getString(), entityId, entity); + updateEntity(player, entity); + + Pair pair = new ImmutablePair<>(entity.getEntityWorld().getRegistryKey().getValue(), entity); + lock.lock(); + playerWatchEntity.put(player, pair); + if (!entityWatchPlayerSet.containsKey(pair)) { + entityWatchPlayerSet.put(pair, new HashSet<>()); + } + entityWatchPlayerSet.get(pair).add(player); + lock.unlock(); + } + } + + private static MutablePair getIdentifierEntityPair(Identifier identifier, Entity entity) { + pairLock.lock(); + identifierEntityPair.setLeft(identifier); + identifierEntityPair.setRight(entity); + pairLock.unlock(); + return identifierEntityPair; + } + + private static MutablePair getIdentifierBlockPosPair(Identifier identifier, BlockPos pos) { + pairLock.lock(); + identifierBlockPosPair.setLeft(identifier); + identifierBlockPosPair.setRight(pos); + pairLock.unlock(); + return identifierBlockPosPair; + } + + // 工具 + private static @Nullable Set getWatchPlayerList(@NotNull Entity entity) { + return entityWatchPlayerSet.get(getIdentifierEntityPair(entity.getEntityWorld().getRegistryKey().getValue(), entity)); + } + + private static @Nullable Set getWatchPlayerList(@NotNull World world, @NotNull BlockPos blockPos) { + return blockPosWatchPlayerSet.get(getIdentifierBlockPosPair(world.getRegistryKey().getValue(), blockPos)); + } + + public static boolean syncEntityToClient(@NotNull Entity entity) { + if (entity.getEntityWorld().isClient()) { + return false; + } + lock.lock(); + Set playerList = getWatchPlayerList(entity); + boolean ret = false; + if (playerList != null) { + for (ServerPlayerEntity player : playerList) { + updateEntity(player, entity); + ret = true; + } + } + lock.unlock(); + return ret; + } + + public static boolean syncBlockEntityToClient(@NotNull BlockEntity blockEntity) { + boolean ret = false; + World world = blockEntity.getWorld(); + BlockPos pos = blockEntity.getPos(); + // 在生成世界时可能会产生空指针 + if (world != null) { + if (world.isClient()) { + return false; + } + BlockState blockState = world.getBlockState(pos); + lock.lock(); + Set playerList = getWatchPlayerList(world, blockEntity.getPos()); + + Set playerListAdj = null; + + if (blockState.getBlock() instanceof ChestBlock) { + if (blockState.get(ChestBlock.CHEST_TYPE) != ChestType.SINGLE) { + // 如果是一个大箱子需要特殊处理 + // 上面不用 isOf 是为了考虑到陷阱箱的情况,陷阱箱继承自箱子 + BlockPos posAdj = pos.offset(ChestBlock.getFacing(blockState)); + playerListAdj = getWatchPlayerList(world, posAdj); + } + } else if (ServerMain.tisCarpetLoaded && blockState.isOf(Blocks.BARREL) && RuleHelper.getBooleanValue(getCarpetRule("largeBarrel"))) { + Direction directionOpposite = blockState.get(BarrelBlock.FACING).getOpposite(); + BlockPos posAdj = pos.offset(directionOpposite); + BlockState blockStateAdj = world.getBlockState(posAdj); + if (blockStateAdj.isOf(Blocks.BARREL) && blockStateAdj.get(BarrelBlock.FACING) == directionOpposite) { + playerListAdj = getWatchPlayerList(world, posAdj); + } + } + if (playerListAdj != null) { + if (playerList == null) { + playerList = playerListAdj; + } else { + playerList.addAll(playerListAdj); + } + } + + if (playerList != null) { + for (ServerPlayerEntity player : playerList) { + updateBlockEntity(player, blockEntity); + ret = true; + } + } + lock.unlock(); + } + return ret; + } + + private static void clearPlayerWatchEntity(ServerPlayerEntity player) { + lock.lock(); + Pair pair = playerWatchEntity.get(player); + if (pair != null) { + Set playerSet = entityWatchPlayerSet.get(pair); + playerSet.remove(player); + if (playerSet.isEmpty()) { + entityWatchPlayerSet.remove(pair); + } + playerWatchEntity.remove(player); + } + lock.unlock(); + } + + private static void clearPlayerWatchBlock(ServerPlayerEntity player) { + lock.lock(); + Pair pair = playerWatchBlockPos.get(player); + if (pair != null) { + Set playerSet = blockPosWatchPlayerSet.get(pair); + playerSet.remove(player); + if (playerSet.isEmpty()) { + blockPosWatchPlayerSet.remove(pair); + } + playerWatchBlockPos.remove(player); + } + lock.unlock(); + } + + // 停用 PcaSyncProtocol + public static void disablePcaSyncProtocolGlobal() { + lock.lock(); + playerWatchBlockPos.clear(); + playerWatchEntity.clear(); + blockPosWatchPlayerSet.clear(); + entityWatchPlayerSet.clear(); + lock.unlock(); + if (ServerMain.server != null) { + for (ServerPlayerEntity player : ServerMain.server.getPlayerManager().getPlayerList()) { + disablePcaSyncProtocol(player); + } + } + } + + // 启用 PcaSyncProtocol + public static void enablePcaSyncProtocolGlobal() { + if (ServerMain.server == null) { + return; + } + for (ServerPlayerEntity player : ServerMain.server.getPlayerManager().getPlayerList()) { + enablePcaSyncProtocol(player); + } + } + + + // 删除玩家数据 + public static void clearPlayerWatchData(ServerPlayerEntity player) { + PcaSyncProtocol.clearPlayerWatchBlock(player); + PcaSyncProtocol.clearPlayerWatchEntity(player); + } + + private static CarpetRule getCarpetRule(String rule){ + return CarpetServer.settingsManager.getCarpetRule(rule); + } +} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/ShulkerBoxItemUtil.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/ShulkerBoxItemUtil.java new file mode 100644 index 0000000..bc8c266 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/ShulkerBoxItemUtil.java @@ -0,0 +1,41 @@ +package com.github.zly2006.carpetslsaddition.util; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; + +public class ShulkerBoxItemUtil { + public static final int SHULKERBOX_MAX_STACK_AMOUNT = 64; + + public static boolean isEmptyShulkerBoxItem(ItemStack itemStack) { + if (itemStack.getItem() instanceof BlockItem && + ((BlockItem) itemStack.getItem()).getBlock() instanceof ShulkerBoxBlock) { + NbtCompound nbt = itemStack.getNbt(); + if (nbt != null && nbt.contains("BlockEntityTag", 10)) { + NbtCompound tag = nbt.getCompound("BlockEntityTag"); + if (tag.contains("Items", 9)) { + NbtList tagList = tag.getList("Items", 10); + return tagList.size() <= 0; + } + } + return true; + } else { + return false; + } + } + + public static int getMaxCount(ItemStack itemStack) { + if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(itemStack)) { + return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT; + } else { + return itemStack.getMaxCount(); + } + } + + public static boolean isStackable(ItemStack itemStack) { + return getMaxCount(itemStack) > 1 && (!itemStack.isDamageable() || !itemStack.isDamaged()); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/SitEntity.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/SitEntity.java new file mode 100644 index 0000000..639bba2 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/SitEntity.java @@ -0,0 +1,7 @@ +package com.github.zly2006.carpetslsaddition.util; + +public interface SitEntity { + boolean isSitEntity(); + + void setSitEntity(boolean sitEntity); +} diff --git a/src/main/resources/assets/slsaddition/lang/en_us.json b/src/main/resources/assets/slsaddition/lang/en_us.json index de2977c..9ef1a67 100644 --- a/src/main/resources/assets/slsaddition/lang/en_us.json +++ b/src/main/resources/assets/slsaddition/lang/en_us.json @@ -1,9 +1,18 @@ { + "carpet.category.pca": "PCA Adaption", + "carpet.category.needClient":"Need client support", + "carpet.category.protocol":"Protocol", "carpet.rule.obtainableReinforcedDeepSlate.desc": "When an anvil falls on 64 dropped deepslates, they will be turned in a reinforced deepslate instead of disappearing.", - "carpet.rule.skipTicksForJoblessVillager.desc": "Villagers will skip ticks if they have no job.", + "carpet.rule.skipTicksForJoblessVillager.desc": "For jobless/nitwit villagers, after performing a set number of tick operations, skip the 1 tick operation.", "carpet.rule.maxVillagersInABlock.desc": "The maximum number of villagers that can be in a block, set to -1 to disable.", "carpet.rule.creativeNoInfinitePickup.desc": "If my inventory is full, don't pick up items in creative mode.", "carpet.rule.autoUrl.desc": "Automatically add click event to URLs in chat.", "carpet.rule.noBatSpawning.desc": "Prevents bats from spawning.", - "carpet.rule.offlineFakePlayers.desc": "Using offline uuid and skin so that fake players wont cause extra network traffic." + "carpet.rule.offlineFakePlayers.desc": "Using offline uuid and skin so that fake players wont cause extra network traffic.", + "carpet.rule.pcaSyncProtocol.desc": "Using PCA protocol to support sync entity and blockEntity from server", + "carpet.rule.pcaSyncPlayerEntity.desc": "Which player entity can be sync", + "carpet.rule.emptyShulkerBoxStack.desc": "Empty shulker boxes stack", + "carpet.rule.useDyeOnShulkerBox.desc": "Dyes can be used on shulker boxes, empty potion will clean color", + "carpet.rule.xaeroWorldName.desc": "set xaero world name to sync word id to xaerominimap, \"#none\" is disable.", + "carpet.rule.playerSit.desc": "Player can sit down when fast sneak 3 times." } diff --git a/src/main/resources/assets/slsaddition/lang/zh_cn.json b/src/main/resources/assets/slsaddition/lang/zh_cn.json index b30e283..59ca5c7 100644 --- a/src/main/resources/assets/slsaddition/lang/zh_cn.json +++ b/src/main/resources/assets/slsaddition/lang/zh_cn.json @@ -1,9 +1,18 @@ { + "carpet.category.pca": "PCA适配", + "carpet.category.needClient": "需要客户端支持", + "carpet.category.protocol": "协议", "carpet.rule.maxVillagersInABlock.desc": "每个方块中最大村民数量,设置-1为无限制", - "carpet.rule.obtainableReinforcedDeepSlate.desc": "当铁砧在64个深板岩的凋落物上时,可以获得强化深板岩", - "carpet.rule.skipTicksForJoblessVillager.desc": "村民没有工作时,跳过村民的tick", + "carpet.rule.obtainableReinforcedDeepSlate.desc": "当铁砧在64个深板岩的掉落物上时,可以获得强化深板岩", + "carpet.rule.skipTicksForJoblessVillager.desc": "对于无业/傻子村民,执行设定数量的tick运算后,跳过1tick的运算", "carpet.rule.creativeNoInfinitePickup.desc": "创造模式下,玩家不能无限制地拾取物品", "carpet.rule.autoUrl.desc": "自动识别URL并添加点击事件", - "carpet.rule.noBatSpawning.desc": "禁止蝙蝠生成", - "carpet.rule.offlineFakePlayers.desc": "假玩家使用离线皮肤和uuid" + "carpet.rule.noBatSpawning.desc": "禁止任何区域蝙蝠生成", + "carpet.rule.offlineFakePlayers.desc": "由Carpet生成的假玩家,将使用离线皮肤和UUID", + "carpet.rule.pcaSyncProtocol.desc": "借助PCA同步协议,支持同步 BlockEntity 和 Entity 到客户端", + "carpet.rule.pcaSyncPlayerEntity.desc": "决定哪些玩家的数据将会被同步", + "carpet.rule.emptyShulkerBoxStack.desc": "空潜影盒可堆叠,但是不会改变容器中潜影盒的堆叠行为", + "carpet.rule.useDyeOnShulkerBox.desc": "可以使用染料直接对地上的潜影盒染色,用水瓶可以洗掉潜影盒的颜色", + "carpet.rule.xaeroWorldName.desc": "设置 Xaero 世界名来同步世界 ID,\"#none\" 表示不同步", + "carpet.rule.playerSit.desc": "在快速潜行 3 次后玩家可以坐下" } diff --git a/src/main/resources/slsaddition.mixins.json b/src/main/resources/slsaddition.mixins.json index 8ab9924..43d1e59 100644 --- a/src/main/resources/slsaddition.mixins.json +++ b/src/main/resources/slsaddition.mixins.json @@ -4,18 +4,37 @@ "package": "com.github.zly2006.carpetslsaddition.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "AccessorPlayerAbilities", - "MixinAbstractSignBlock", - "MixinBlock", - "MixinFakePlayer", - "MixinFallingBlockEntity", - "MixinItem", - "MixinItemEntity", - "MixinPlayerEntity", - "MixinPlayerInventory", - "MixinSettingsManager", - "MixinSpawnHelper", - "MixinVillagerEntity" + "block.MixinAbstractSignBlock", + "block.MixinBlock", + "carpet.MixinFakePlayer", + "carpet.MixinSettingsManager", + "entity.MixinArmorStandEntity", + "entity.MixinFallingBlockEntity", + "entity.MixinItemEntity", + "entity.MixinPlayerEntity", + "entity.MixinServerPlayerEntity", + "entity.MixinVillagerEntity", + "item.MixinDyeItem", + "item.MixinItem", + "item.MixinPotionItem", + "player.AccessorPlayerAbilities", + "player.MixinPlayerInventory", + "protocol.pcaSyncProtocol.block.MixinAbstractFurnaceBlockEntity", + "protocol.pcaSyncProtocol.block.MixinBarrelBlockEntity", + "protocol.pcaSyncProtocol.block.MixinBeehiveBlockEntity", + "protocol.pcaSyncProtocol.block.MixinBrewingStandBlockEntity", + "protocol.pcaSyncProtocol.block.MixinChestBlockEntity", + "protocol.pcaSyncProtocol.block.MixinComparatorBlockEntity", + "protocol.pcaSyncProtocol.block.MixinDispenserBlockEntity", + "protocol.pcaSyncProtocol.block.MixinHopperBlockEntity", + "protocol.pcaSyncProtocol.block.MixinShulkerBoxBlockEntity", + "protocol.pcaSyncProtocol.entity.MixinHorseBaseEntity", + "protocol.pcaSyncProtocol.entity.MixinMerchantEntity", + "protocol.pcaSyncProtocol.entity.MixinStorageMinecartEntity", + "screen.MixinScreenHandler", + "screen.MixinSlot", + "server.MixinPlayerManager", + "world.MixinSpawnHelper" ], "client": [ ],