feat: 从PCA中迁移部分功能

This commit is contained in:
Disy authored and zly2006 committed 2024-07-11 12:14:01 +08:00
1 parent 9dc306f18a
commit f30e7425e6
39 files changed
+1354 -49

No files matched your search

@@ -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
}
}
@@ -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);
}
}
@@ -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;
@@ -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;
@@ -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<GameProfile> onCreate(UserCache instance, String name) {
@@ -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();
@@ -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<? extends LivingEntity> 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");
}
}
}
@@ -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;
@@ -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;
@@ -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;
@@ -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));
}
}
}
}
@@ -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) {
@@ -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;
}
}
@@ -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;
@@ -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<ActionResult> 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));
}
}
}
@@ -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;
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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<BeehiveBlockEntity.Bee> 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<List<Entity>> 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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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<Boolean> 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);
}
}
}
@@ -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);
}
}
}
@@ -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<? extends AnimalEntity> 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.");
}
}
}
@@ -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<? extends PassiveEntity> entityType, World world) {
super(entityType, world);
}
@Inject(method = "<init>(Lnet/minecraft/entity/EntityType;Lnet/minecraft/world/World;)V", at = @At(value = "RETURN"))
private void addInventoryListener(EntityType<? extends MerchantEntity> 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.");
}
}
}
@@ -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.");
}
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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()));
}
}
@@ -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;
@@ -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<ServerPlayerEntity, Pair<Identifier, BlockPos>> playerWatchBlockPos = new HashMap<>();
private static final Map<ServerPlayerEntity, Pair<Identifier, Entity>> playerWatchEntity = new HashMap<>();
private static final Map<Pair<Identifier, BlockPos>, Set<ServerPlayerEntity>> blockPosWatchPlayerSet = new HashMap<>();
private static final Map<Pair<Identifier, Entity>, Set<ServerPlayerEntity>> entityWatchPlayerSet = new HashMap<>();
private static final MutablePair<Identifier, Entity> identifierEntityPair = new MutablePair<>();
private static final MutablePair<Identifier, BlockPos> 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<Identifier, BlockPos> 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<Identifier, Entity> 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<Identifier, Entity> getIdentifierEntityPair(Identifier identifier, Entity entity) {
pairLock.lock();
identifierEntityPair.setLeft(identifier);
identifierEntityPair.setRight(entity);
pairLock.unlock();
return identifierEntityPair;
}
private static MutablePair<Identifier, BlockPos> getIdentifierBlockPosPair(Identifier identifier, BlockPos pos) {
pairLock.lock();
identifierBlockPosPair.setLeft(identifier);
identifierBlockPosPair.setRight(pos);
pairLock.unlock();
return identifierBlockPosPair;
}
// 工具
private static @Nullable Set<ServerPlayerEntity> getWatchPlayerList(@NotNull Entity entity) {
return entityWatchPlayerSet.get(getIdentifierEntityPair(entity.getEntityWorld().getRegistryKey().getValue(), entity));
}
private static @Nullable Set<ServerPlayerEntity> 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<ServerPlayerEntity> 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<ServerPlayerEntity> playerList = getWatchPlayerList(world, blockEntity.getPos());
Set<ServerPlayerEntity> 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<Identifier, Entity> pair = playerWatchEntity.get(player);
if (pair != null) {
Set<ServerPlayerEntity> 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<Identifier, BlockPos> pair = playerWatchBlockPos.get(player);
if (pair != null) {
Set<ServerPlayerEntity> 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);
}
}
@@ -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());
}
}
@@ -0,0 +1,7 @@
package com.github.zly2006.carpetslsaddition.util;
public interface SitEntity {
boolean isSitEntity();
void setSitEntity(boolean sitEntity);
}