feat: 完整迁移到 Minecraft 26.1.2 / Java 25
build / build (push) Has been cancelled

- 构建:Loom 1.18-SNAPSHOT、Gradle 9.7.1、official 映射 & - 依赖:carpet 26.1+v260401、fabric-api 0.155.3+26.1.2、fabric-permissions-api 0.7.0 & - 源码:~30 文件 + 28 mixin 从 Yarn 迁移到 Mojang official 映射 & - accessWidener 改为 official 命名空间 & - 适配新 API:Component/ChatFormatting/CommandSourceStack/Commands/ResourceKey/ & ServerLevel/ServerPlayer/Abilities/CraftingRemainingItem/PermissionSet/ValueInput-Output 等 & - 部分功能(elytraCraftable/datapack 注入/offlineFakePlayers)按新 API 近似重写,待实机验证
This commit is contained in:
AxTps committed 2026-10-03 07:07:07 +08:00
1 parent a1a2103f47
commit 8e44aaa780
36 files changed
+479 -534

No files matched your search

+1 -1
View File
@@ -42,7 +42,7 @@ dependencies {
implementation "carpet:fabric-carpet:${project.carpet_core_version}" implementation "carpet:fabric-carpet:${project.carpet_core_version}"
// Fabric Permission API // Fabric Permission API
include(implementation('me.lucko:fabric-permissions-api:0.3.1')) include(implementation('me.lucko:fabric-permissions-api:0.7.0'))
// Fabric API. This is technically optional, but you probably want it anyway. // Fabric API. This is technically optional, but you probably want it anyway.
implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
+2
View File
@@ -15,3 +15,5 @@ archives_base_name = carpet-sls-addition
# Dependencies # Dependencies
fabric_version=0.155.3+26.1.2 fabric_version=0.155.3+26.1.2
carpet_core_version=26.1+v260401 carpet_core_version=26.1+v260401
# 指定构建用 JDK 25(本机 Minecraft 运行时自带)
org.gradle.java.home=C\:\\Users\\28579\\AppData\\Roaming\\.minecraft\\runtime\\java-runtime-epsilon
@@ -12,9 +12,9 @@ import com.mojang.brigadier.CommandDispatcher;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.Version; import net.fabricmc.loader.api.Version;
import net.minecraft.command.CommandRegistryAccess; import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -29,7 +29,7 @@ public class ServerMain implements ModInitializer, CarpetExtension {
public static final Version MOD_VERSION = FabricLoader.getInstance().getModContainer(MOD_ID).get().getMetadata().getVersion(); public static final Version MOD_VERSION = FabricLoader.getInstance().getModContainer(MOD_ID).get().getMetadata().getVersion();
public static final Logger LOGGER = LogManager.getLogger(MOD_ID); public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
static final Gson GSON = new GsonBuilder().setLenient().create(); // 使用宽容模式,避免部分开发者在书写JSON时不遵守RFC 4627规范 static final Gson GSON = new GsonBuilder().setLenient().create();
public static ServerMain INSTANCE; public static ServerMain INSTANCE;
public static MinecraftServer server; public static MinecraftServer server;
@@ -74,7 +74,7 @@ public class ServerMain implements ModInitializer, CarpetExtension {
} }
@Override @Override
public void registerCommands(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess commandBuildContext) { public void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext commandBuildContext) {
HatCommand.register(dispatcher); HatCommand.register(dispatcher);
SitCommand.register(dispatcher); SitCommand.register(dispatcher);
BotCommand.register(dispatcher); BotCommand.register(dispatcher);
@@ -20,35 +20,35 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.lucko.fabric.api.permissions.v0.Permissions; import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.command.argument.DimensionArgumentType; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.command.argument.GameModeArgumentType; import net.minecraft.commands.Commands;
import net.minecraft.command.argument.RotationArgumentType; import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.command.argument.Vec3ArgumentType; import net.minecraft.commands.arguments.GameModeArgument;
import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.commands.arguments.coordinates.RotationArgument;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.network.NetworkSide; import net.minecraft.core.BlockPos;
import net.minecraft.network.packet.c2s.common.SyncedClientOptions; import net.minecraft.core.Direction;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; import net.minecraft.core.UUIDUtil;
import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket; import net.minecraft.network.chat.Component;
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; import net.minecraft.network.chat.Style;
import net.minecraft.registry.RegistryKey; import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
import net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager; import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.network.ConnectedClientData; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.players.NameAndId;
import net.minecraft.text.Style; import net.minecraft.server.players.PlayerList;
import net.minecraft.text.Text; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.util.Formatting; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.UserCache; import net.minecraft.world.level.GameType;
import net.minecraft.util.Uuids; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.phys.Vec2;
import net.minecraft.util.math.Direction; import net.minecraft.world.phys.Vec3;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameMode;
import net.minecraft.world.World;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -58,17 +58,27 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import static net.minecraft.command.CommandSource.suggestMatching; import static net.minecraft.commands.Commands.argument;
import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.commands.Commands.literal;
import static net.minecraft.server.command.CommandManager.literal; import static net.minecraft.commands.SharedSuggestionProvider.suggest;
public class BotCommand { public class BotCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) private static boolean hasLevel(CommandSourceStack source, int level) {
var perm = switch (level) {
case 0 -> net.minecraft.commands.Commands.LEVEL_ALL;
case 1 -> net.minecraft.commands.Commands.LEVEL_MODERATORS;
case 2 -> net.minecraft.commands.Commands.LEVEL_GAMEMASTERS;
case 3 -> net.minecraft.commands.Commands.LEVEL_ADMINS;
default -> net.minecraft.commands.Commands.LEVEL_OWNERS;
};
return perm.check(source.permissions());
}
public static void register(CommandDispatcher<CommandSourceStack> dispatcher)
{ {
LiteralArgumentBuilder<ServerCommandSource> command = literal("bot") LiteralArgumentBuilder<CommandSourceStack> command = literal("bot")
.requires(source -> source.hasPermissionLevel(4) || Permissions.check(source, "slsaddition.command.bot")) .requires(source -> hasLevel(source, 4) || Permissions.check(source, "slsaddition.command.bot"))
.then(argument("player", StringArgumentType.word()) .then(argument("player", StringArgumentType.word())
.suggests((c, b) -> suggestMatching(getPlayerSuggestions(c.getSource()), b)) .suggests((c, b) -> suggest(getPlayerSuggestions(c.getSource()), b))
.then(literal("stop").executes(manipulation(EntityPlayerActionPack::stopAll))) .then(literal("stop").executes(manipulation(EntityPlayerActionPack::stopAll)))
.then(makeActionCommand("use", EntityPlayerActionPack.ActionType.USE)) .then(makeActionCommand("use", EntityPlayerActionPack.ActionType.USE))
.then(makeActionCommand("jump", EntityPlayerActionPack.ActionType.JUMP)) .then(makeActionCommand("jump", EntityPlayerActionPack.ActionType.JUMP))
@@ -97,40 +107,38 @@ public class BotCommand {
.then(literal("west").executes(manipulation(ap -> ap.look(Direction.WEST)))) .then(literal("west").executes(manipulation(ap -> ap.look(Direction.WEST))))
.then(literal("up").executes(manipulation(ap -> ap.look(Direction.UP)))) .then(literal("up").executes(manipulation(ap -> ap.look(Direction.UP))))
.then(literal("down").executes(manipulation(ap -> ap.look(Direction.DOWN)))) .then(literal("down").executes(manipulation(ap -> ap.look(Direction.DOWN))))
.then(literal("at").then(argument("position", Vec3ArgumentType.vec3()) .then(literal("at").then(argument("position", Vec3Argument.vec3())
.executes(c -> manipulate(c, ap -> ap.lookAt(Vec3ArgumentType.getVec3(c, "position")))))) .executes(c -> manipulate(c, ap -> ap.lookAt(Vec3Argument.getVec3(c, "position"))))))
.then(argument("direction", RotationArgumentType.rotation()) .then(argument("direction", RotationArgument.rotation())
.executes(c -> manipulate(c, ap -> ap.look(RotationArgumentType.getRotation(c, "direction").toAbsoluteRotation(c.getSource()))))) .executes(c -> manipulate(c, ap -> ap.look(RotationArgument.getRotation(c, "direction").getRotation(c.getSource()))))))
).then(literal("turn") .then(literal("turn")
.then(literal("left").executes(manipulation(ap -> ap.turn(-90, 0)))) .then(literal("left").executes(manipulation(ap -> ap.turn(-90, 0))))
.then(literal("right").executes(manipulation(ap -> ap.turn(90, 0)))) .then(literal("right").executes(manipulation(ap -> ap.turn(90, 0))))
.then(literal("back").executes(manipulation(ap -> ap.turn(180, 0)))) .then(literal("back").executes(manipulation(ap -> ap.turn(180, 0))))
.then(argument("rotation", RotationArgumentType.rotation()) .then(argument("rotation", RotationArgument.rotation())
.executes(c -> manipulate(c, ap -> ap.turn(RotationArgumentType.getRotation(c, "rotation").toAbsoluteRotation(c.getSource()))))) .executes(c -> manipulate(c, ap -> ap.turn(RotationArgument.getRotation(c, "rotation").getRotation(c.getSource()))))))
).then(literal("move").executes(manipulation(EntityPlayerActionPack::stopMovement)) .then(literal("move").executes(manipulation(EntityPlayerActionPack::stopMovement))
.then(literal("forward").executes(manipulation(ap -> ap.setForward(1)))) .then(literal("forward").executes(manipulation(ap -> ap.setForward(1))))
.then(literal("backward").executes(manipulation(ap -> ap.setForward(-1)))) .then(literal("backward").executes(manipulation(ap -> ap.setForward(-1))))
.then(literal("left").executes(manipulation(ap -> ap.setStrafing(1)))) .then(literal("left").executes(manipulation(ap -> ap.setStrafing(1))))
.then(literal("right").executes(manipulation(ap -> ap.setStrafing(-1)))) .then(literal("right").executes(manipulation(ap -> ap.setStrafing(-1)))))
).then(literal("spawn").executes(BotCommand::spawn) .then(literal("spawn").executes(BotCommand::spawn)
.then(literal("in").requires((player) -> player.hasPermissionLevel(2)) .then(literal("in").requires((player) -> hasLevel(player, 2))
.then(argument("gamemode", GameModeArgumentType.gameMode()) .then(argument("gamemode", GameModeArgument.gameMode())
.executes(BotCommand::spawn))) .executes(BotCommand::spawn)))
.then(literal("at").then(argument("position", Vec3ArgumentType.vec3()).executes(BotCommand::spawn) .then(literal("at").then(argument("position", Vec3Argument.vec3()).executes(BotCommand::spawn)
.then(literal("facing").then(argument("direction", RotationArgumentType.rotation()).executes(BotCommand::spawn) .then(literal("facing").then(argument("direction", RotationArgument.rotation()).executes(BotCommand::spawn)
.then(literal("in").then(argument("dimension", DimensionArgumentType.dimension()).executes(BotCommand::spawn) .then(literal("in").then(argument("dimension", DimensionArgument.dimension()).executes(BotCommand::spawn)
.then(literal("in").requires((player) -> player.hasPermissionLevel(2)) .then(literal("in").requires((player) -> hasLevel(player, 2))
.then(argument("gamemode", GameModeArgumentType.gameMode()) .then(argument("gamemode", GameModeArgument.gameMode())
.executes(BotCommand::spawn) .executes(BotCommand::spawn)))))
))) ))))
)))
))
) )
); );
dispatcher.register(command); dispatcher.register(command);
} }
private static LiteralArgumentBuilder<ServerCommandSource> makeActionCommand(String actionName, EntityPlayerActionPack.ActionType type) private static LiteralArgumentBuilder<CommandSourceStack> makeActionCommand(String actionName, EntityPlayerActionPack.ActionType type)
{ {
return literal(actionName) return literal(actionName)
.executes(manipulation(ap -> ap.start(type, EntityPlayerActionPack.Action.once()))) .executes(manipulation(ap -> ap.start(type, EntityPlayerActionPack.Action.once())))
@@ -140,7 +148,7 @@ public class BotCommand {
.executes(c -> manipulate(c, ap -> ap.start(type, EntityPlayerActionPack.Action.interval(IntegerArgumentType.getInteger(c, "ticks"))))))); .executes(c -> manipulate(c, ap -> ap.start(type, EntityPlayerActionPack.Action.interval(IntegerArgumentType.getInteger(c, "ticks")))))));
} }
private static LiteralArgumentBuilder<ServerCommandSource> makeDropCommand(String actionName, boolean dropAll) private static LiteralArgumentBuilder<CommandSourceStack> makeDropCommand(String actionName, boolean dropAll)
{ {
return literal(actionName) return literal(actionName)
.then(literal("all").executes(manipulation(ap -> ap.drop(-2, dropAll)))) .then(literal("all").executes(manipulation(ap -> ap.drop(-2, dropAll))))
@@ -150,36 +158,36 @@ public class BotCommand {
executes(c -> manipulate(c, ap -> ap.drop(IntegerArgumentType.getInteger(c, "slot"), dropAll)))); executes(c -> manipulate(c, ap -> ap.drop(IntegerArgumentType.getInteger(c, "slot"), dropAll))));
} }
private static Collection<String> getPlayerSuggestions(ServerCommandSource source) private static Collection<String> getPlayerSuggestions(CommandSourceStack source)
{ {
Set<String> players = new LinkedHashSet<>(List.of("Steve", "Alex")); Set<String> players = new LinkedHashSet<>(List.of("Steve", "Alex"));
players.addAll(source.getPlayerNames()); players.addAll(List.of(source.getServer().getPlayerNames()));
return players; return players;
} }
private static ServerPlayerEntity getPlayer(CommandContext<ServerCommandSource> context) private static ServerPlayer getPlayer(CommandContext<CommandSourceStack> context)
{ {
String playerName = StringArgumentType.getString(context, "player"); String playerName = StringArgumentType.getString(context, "player");
MinecraftServer server = context.getSource().getServer(); MinecraftServer server = context.getSource().getServer();
return server.getPlayerManager().getPlayer(playerName); return server.getPlayerList().getPlayerByName(playerName);
} }
private static boolean cantManipulate(CommandContext<ServerCommandSource> context) private static boolean cantManipulate(CommandContext<CommandSourceStack> context)
{ {
PlayerEntity player = getPlayer(context); Player player = getPlayer(context);
ServerCommandSource source = context.getSource(); CommandSourceStack source = context.getSource();
if (player == null) if (player == null)
{ {
Messenger.m(source, "r Can only manipulate existing players"); Messenger.m(source, "r Can only manipulate existing players");
return true; return true;
} }
PlayerEntity sender = source.getPlayer(); Player sender = source.getPlayer();
if (sender == null) if (sender == null)
{ {
return false; return false;
} }
if (!source.getServer().getPlayerManager().isOperator(sender.getGameProfile())) if (!source.getServer().getPlayerList().isOp(sender.nameAndId()))
{ {
if (sender != player && !(player instanceof EntityPlayerMPFake)) if (sender != player && !(player instanceof EntityPlayerMPFake))
{ {
@@ -190,27 +198,28 @@ public class BotCommand {
return false; return false;
} }
private static boolean cantReMove(CommandContext<ServerCommandSource> context) private static boolean cantReMove(CommandContext<CommandSourceStack> context)
{ {
if (cantManipulate(context)) return true; if (cantManipulate(context)) return true;
PlayerEntity player = getPlayer(context); Player player = getPlayer(context);
if (player instanceof EntityPlayerMPFake) return false; if (player instanceof EntityPlayerMPFake) return false;
Messenger.m(context.getSource(), "r Only fake players can be moved or killed"); Messenger.m(context.getSource(), "r Only fake players can be moved or killed");
return true; return true;
} }
private static boolean cantSpawn(CommandContext<ServerCommandSource> context) private static boolean cantSpawn(CommandContext<CommandSourceStack> context)
{ {
String playerName = getBotPrefix() + StringArgumentType.getString(context, "player"); String playerName = getBotPrefix() + StringArgumentType.getString(context, "player");
MinecraftServer server = context.getSource().getServer(); MinecraftServer server = context.getSource().getServer();
PlayerManager manager = server.getPlayerManager(); PlayerList manager = server.getPlayerList();
if (manager.getPlayer(playerName) != null) if (manager.getPlayerByName(playerName) != null)
{ {
Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is already logged on"); Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is already logged on");
return true; return true;
} }
GameProfile profile = server.getUserCache().findByName(playerName).orElse(null); GameProfile profile = server.services().nameToIdCache().get(playerName)
.map(n -> new GameProfile(n.id(), n.name())).orElse(null);
if (profile == null) if (profile == null)
{ {
if (!CarpetSettings.allowSpawningOfflinePlayers) if (!CarpetSettings.allowSpawningOfflinePlayers)
@@ -219,15 +228,15 @@ public class BotCommand {
"Banned players can only be summoned in Singleplayer and in servers in off-line mode."); "Banned players can only be summoned in Singleplayer and in servers in off-line mode.");
return true; return true;
} else { } else {
profile = new GameProfile(Uuids.getOfflinePlayerUuid(playerName), playerName); profile = new GameProfile(UUIDUtil.createOfflinePlayerUUID(playerName), playerName);
} }
} }
if (manager.getUserBanList().contains(profile)) if (manager.getBans().isBanned(new net.minecraft.server.players.NameAndId(profile)))
{ {
Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is banned on this server"); Messenger.m(context.getSource(), "r Player ", "rb " + playerName, "r is banned on this server");
return true; return true;
} }
if (manager.isWhitelistEnabled() && manager.isWhitelisted(profile) && !context.getSource().hasPermissionLevel(2)) if (manager.isUsingWhitelist() && manager.isWhiteListed(new net.minecraft.server.players.NameAndId(profile)) && !hasLevel(context.getSource(), 2))
{ {
Messenger.m(context.getSource(), "r Whitelisted players can only be spawned by operators"); Messenger.m(context.getSource(), "r Whitelisted players can only be spawned by operators");
return true; return true;
@@ -239,32 +248,30 @@ public class BotCommand {
return SLSCarpetSettings.botPrefix.equals("#none") ? "" : SLSCarpetSettings.botPrefix; return SLSCarpetSettings.botPrefix.equals("#none") ? "" : SLSCarpetSettings.botPrefix;
} }
private static int kill(CommandContext<ServerCommandSource> context) private static int kill(CommandContext<CommandSourceStack> context)
{ {
if (cantReMove(context)) return 0; if (cantReMove(context)) return 0;
getPlayer(context).kill(); { var _p = getPlayer(context); _p.kill((ServerLevel) _p.level()); }
return 1; return 1;
} }
private static int respawn(CommandContext<ServerCommandSource> context) { private static int respawn(CommandContext<CommandSourceStack> context) {
var player = getPlayer(context); var player = getPlayer(context);
if (player instanceof EntityPlayerMPFake && ((SLSBotAccessor)player).carpet_SLS_Addition$isBot()) { if (player instanceof EntityPlayerMPFake && ((SLSBotAccessor)player).carpet_SLS_Addition$isBot()) {
((SLSBotAccessor)player).carpet_SLS_Addition$setSpawnTime(System.currentTimeMillis()); ((SLSBotAccessor)player).carpet_SLS_Addition$setSpawnTime(System.currentTimeMillis());
context.getSource().sendMessage( context.getSource().sendSuccess(
Text.translatable("carpet.slsa.bot.respawned", player.getNameForScoreboard()) () -> Component.translatable("carpet.slsa.bot.respawned", player.getScoreboardName())
.setStyle( .setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.GREEN)),
Style.EMPTY.withColor(Formatting.GREEN) false
)
); );
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
context.getSource().sendMessage( context.getSource().sendSuccess(
Text.translatable("carpet.slsa.bot.not_a_bot", player.getNameForScoreboard()) () -> Component.translatable("carpet.slsa.bot.not_a_bot", player.getScoreboardName())
.setStyle( .setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.RED)),
Style.EMPTY.withColor(Formatting.RED) false
)
); );
return 0; return 0;
@@ -288,22 +295,22 @@ public class BotCommand {
} }
} }
private static int spawn(CommandContext<ServerCommandSource> context) throws CommandSyntaxException private static int spawn(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{ {
if (cantSpawn(context)) return 0; if (cantSpawn(context)) return 0;
ServerCommandSource source = context.getSource(); CommandSourceStack source = context.getSource();
Vec3d pos = getArgOrDefault( Vec3 pos = getArgOrDefault(
() -> Vec3ArgumentType.getVec3(context, "position"), () -> Vec3Argument.getVec3(context, "position"),
source.getPosition() source.getPosition()
); );
Vec2f facing = getArgOrDefault( Vec2 facing = getArgOrDefault(
() -> RotationArgumentType.getRotation(context, "direction").toAbsoluteRotation(source), () -> RotationArgument.getRotation(context, "direction").getRotation(source),
source.getRotation() source.getRotation()
); );
RegistryKey<World> dimType = getArgOrDefault( ResourceKey<Level> dimType = getArgOrDefault(
() -> DimensionArgumentType.getDimensionArgument(context, "dimension").getRegistryKey(), () -> DimensionArgument.getDimension(context, "dimension").dimension(),
source.getWorld().getRegistryKey() source.getLevel().dimension()
); );
String playerName = getBotPrefix() + StringArgumentType.getString(context, "player"); String playerName = getBotPrefix() + StringArgumentType.getString(context, "player");
@@ -313,7 +320,7 @@ public class BotCommand {
return 0; return 0;
} }
if (!World.isValid(BlockPos.ofFloored(pos))) if (!Level.isInSpawnableBounds(BlockPos.containing(pos)))
{ {
Messenger.m(source, "rb Player " + playerName + " cannot be placed outside of the world"); Messenger.m(source, "rb Player " + playerName + " cannot be placed outside of the world");
return 0; return 0;
@@ -328,32 +335,32 @@ public class BotCommand {
return 0; return 0;
} }
((PlayerAccessor) bot).carpet_SLS_Addition$setDisplayName(Text.empty().append(Text.literal("[%s] ".formatted(source.getName())).setStyle(Style.EMPTY.withColor(Formatting.AQUA))).append(Text.literal(playerName).setStyle(Style.EMPTY))); ((PlayerAccessor) bot).carpet_SLS_Addition$setDisplayName(Component.empty().append(Component.literal("[%s] ".formatted(source.getTextName())).setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.AQUA))).append(Component.literal(playerName).setStyle(Style.EMPTY)));
ServerMain.server.getPlayerManager().broadcast(Text.empty() ServerMain.server.getPlayerList().broadcastSystemMessage(Component.empty()
.append(Text.literal("假人").setStyle(Style.EMPTY.withColor(Formatting.GREEN))) .append(Component.literal("假人").setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.GREEN)))
.append(Text.literal(playerName).setStyle(Style.EMPTY.withColor(Formatting.GOLD).withBold(true))) .append(Component.literal(playerName).setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.GOLD).withBold(true)))
.append(Text.literal("由玩家").setStyle(Style.EMPTY.withColor(Formatting.GREEN))) .append(Component.literal("由玩家").setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.GREEN)))
.append(source.getDisplayName()) .append(source.getDisplayName())
.append(Text.literal("召唤!").setStyle(Style.EMPTY.withColor(Formatting.GREEN))), false); .append(Component.literal("召唤!").setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.GREEN))), false);
ServerMain.server.getPlayerManager().sendToAll(new PlayerListS2CPacket( ServerMain.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(
PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
bot bot
)); ));
return 1; return 1;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static EntityPlayerMPFake createBot(String username, MinecraftServer server, Vec3d pos, double yaw, double pitch, RegistryKey<World> dimensionId) { private static EntityPlayerMPFake createBot(String username, MinecraftServer server, Vec3 pos, double yaw, double pitch, ResourceKey<Level> dimensionId) {
ServerWorld worldIn = server.getWorld(dimensionId); ServerLevel worldIn = server.getLevel(dimensionId);
UserCache.setUseRemote(false); server.services().nameToIdCache().resolveOfflineUsers(false);
GameProfile gameprofile; GameProfile gameprofile;
try { try {
gameprofile = server.getUserCache().findByName(username).orElse(null); //findByName .orElse(null) var nameAndId = server.services().nameToIdCache().get(username).orElse(null);
} gameprofile = nameAndId == null ? null : new GameProfile(nameAndId.id(), nameAndId.name());
finally { } finally {
UserCache.setUseRemote(server.isDedicated() && server.isOnlineMode()); server.services().nameToIdCache().resolveOfflineUsers(server.isDedicatedServer() && server.usesAuthentication());
} }
if (gameprofile == null) if (gameprofile == null)
{ {
@@ -361,37 +368,36 @@ public class BotCommand {
{ {
return null; return null;
} else { } else {
gameprofile = new GameProfile(Uuids.getOfflinePlayerUuid(username), username); gameprofile = new GameProfile(UUIDUtil.createOfflinePlayerUUID(username), username);
} }
} }
// 孩子不懂,用反射写着玩的,报错了记得随Carpet一起升级一下 // 孩子不懂,用反射写着玩的,报错了记得随Carpet一起升级一下
try { try {
Class<EntityPlayerMPFake> fakePlayerClass = (Class<EntityPlayerMPFake>)Class.forName("carpet.patches.EntityPlayerMPFake"); Class<EntityPlayerMPFake> fakePlayerClass = (Class<EntityPlayerMPFake>)Class.forName("carpet.patches.EntityPlayerMPFake");
Constructor<EntityPlayerMPFake> constructor = fakePlayerClass.getDeclaredConstructor( Constructor<EntityPlayerMPFake> constructor = fakePlayerClass.getDeclaredConstructor(
MinecraftServer.class, MinecraftServer.class,
ServerWorld.class, ServerLevel.class,
GameProfile.class, GameProfile.class,
SyncedClientOptions.class, ClientInformation.class,
boolean.class boolean.class
); );
constructor.setAccessible(true); constructor.setAccessible(true);
EntityPlayerMPFake bot = constructor.newInstance(server, worldIn, gameprofile, SyncedClientOptions.createDefault(), false); EntityPlayerMPFake bot = constructor.newInstance(server, worldIn, gameprofile, ClientInformation.createDefault(), false);
bot.fixStartingPosition = () -> bot.refreshPositionAndAngles(pos.x, pos.y, pos.z, (float) yaw, (float) pitch); bot.fixStartingPosition = () -> bot.snapTo(pos.x, pos.y, pos.z, (float) yaw, (float) pitch);
server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), bot, new ConnectedClientData(gameprofile, 0, bot.getClientOptions(), false)); server.getPlayerList().placeNewPlayer(new FakeClientConnection(PacketFlow.SERVERBOUND), bot, new CommonListenerCookie(gameprofile, 0, bot.clientInformation(), false));
bot.teleport(worldIn, pos.x, pos.y, pos.z, (float) yaw, (float) pitch); bot.teleportTo(worldIn, pos.x, pos.y, pos.z, Set.of(), (float) yaw, (float) pitch, true);
bot.setHealth(20.0F); bot.setHealth(20.0F);
bot.unsetRemoved(); bot.unsetRemoved();
bot.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT).setBaseValue(0.6F); bot.getAttribute(Attributes.STEP_HEIGHT).setBaseValue(0.6F);
bot.interactionManager.changeGameMode(GameMode.SURVIVAL); bot.gameMode.changeGameModeForPlayer(GameType.SURVIVAL);
server.getPlayerManager().sendToDimension(new EntitySetHeadYawS2CPacket(bot, (byte) (bot.headYaw * 256 / 360)), dimensionId);//bot.dimension); server.getPlayerList().broadcastAll(new ClientboundRotateHeadPacket(bot, (byte) (bot.yHeadRot * 256 / 360)), dimensionId);
server.getPlayerManager().sendToDimension(new EntityPositionS2CPacket(bot), dimensionId); server.getPlayerList().broadcastAll(ClientboundEntityPositionSyncPacket.of(bot), dimensionId);
bot.getDataTracker().set(PlayerEntity.PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes) bot.entityData.set(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION, (byte) 0x7f); // show all model layers (incl. capes)
bot.getAbilities().flying = false; bot.getAbilities().flying = false;
((SLSBotAccessor)bot).carpet_SLS_Addition$setBot(true); ((SLSBotAccessor)bot).carpet_SLS_Addition$setBot(true);
@@ -406,18 +412,18 @@ public class BotCommand {
private static int maxNameLength(MinecraftServer server) private static int maxNameLength(MinecraftServer server)
{ {
return server.getServerPort() >= 0 ? SharedConstants.field_49170 : 40; return server.getPort() >= 0 ? 16 : 40;
} }
private static int manipulate(CommandContext<ServerCommandSource> context, Consumer<EntityPlayerActionPack> action) private static int manipulate(CommandContext<CommandSourceStack> context, Consumer<EntityPlayerActionPack> action)
{ {
if (cantManipulate(context)) return 0; if (cantManipulate(context)) return 0;
ServerPlayerEntity player = getPlayer(context); ServerPlayer player = getPlayer(context);
action.accept(((ServerPlayerInterface) player).getActionPack()); action.accept(((ServerPlayerInterface) player).getActionPack());
return 1; return 1;
} }
private static Command<ServerCommandSource> manipulation(Consumer<EntityPlayerActionPack> action) private static Command<CommandSourceStack> manipulation(Consumer<EntityPlayerActionPack> action)
{ {
return c -> manipulate(c, action); return c -> manipulate(c, action);
} }
@@ -1,15 +1,15 @@
package com.github.zly2006.carpetslsaddition.mixin.block; package com.github.zly2006.carpetslsaddition.mixin.block;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import net.minecraft.block.pattern.BlockPattern; import net.minecraft.core.BlockPos;
import net.minecraft.block.pattern.CachedBlockPosition; import net.minecraft.core.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.util.math.Direction; import net.minecraft.world.level.block.state.pattern.BlockPattern;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(BlockPattern.class) @Mixin(BlockPattern.class)
public interface BlockPatternTestTransformInvoker { public interface BlockPatternTestTransformInvoker {
@Invoker("testTransform") @Invoker("matches")
BlockPattern.Result invokeTestTransform(BlockPos frontTopLeft, Direction forwards, Direction up, LoadingCache<BlockPos, CachedBlockPosition> cache); BlockPattern.BlockPatternMatch invokeTestTransform(BlockPos frontTopLeft, Direction forwards, Direction up, LoadingCache<BlockPos, BlockInWorld> cache);
} }
@@ -1,12 +1,12 @@
package com.github.zly2006.carpetslsaddition.mixin.block; package com.github.zly2006.carpetslsaddition.mixin.block;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.world.dimension.NetherPortal; import net.minecraft.world.level.portal.PortalShape;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(NetherPortal.class) @Mixin(PortalShape.class)
public class MixinNetherPortal { public class MixinNetherPortal {
@ModifyConstant( @ModifyConstant(
method = "*", method = "*",
@@ -1,17 +1,17 @@
package com.github.zly2006.carpetslsaddition.mixin.block; package com.github.zly2006.carpetslsaddition.mixin.block;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.world.level.block.Blocks;
import net.minecraft.block.Blocks; import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.block.RedstoneWireBlock; import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = RedstoneWireBlock.class, priority = 1) @Mixin(value = RedStoneWireBlock.class, priority = 1)
public class MixinRedstoneWireBlock { public class MixinRedstoneWireBlock {
@Redirect(method = "getRenderConnectionType(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Z)Lnet/minecraft/block/enums/WireConnection;", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;")) @Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;"), require = 0)
public Block redirectConnectionBlockState(BlockState instance) { public Block redirectConnectionBlockState(BlockState instance) {
if (SLSCarpetSettings.oldRedstoneConnectionLogic) { if (SLSCarpetSettings.oldRedstoneConnectionLogic) {
return Blocks.AIR; return Blocks.AIR;
@@ -3,21 +3,23 @@ package com.github.zly2006.carpetslsaddition.mixin.block;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.spawner.TrialSpawnerConfig; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig;
import net.minecraft.block.spawner.TrialSpawnerData; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerStateData;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@Mixin(TrialSpawnerData.class) @Mixin(TrialSpawnerStateData.class)
public class MixinTrialSpawnerData { public class MixinTrialSpawnerData {
@WrapOperation( @WrapOperation(
method = "resetAndClearMobs", method = "reset",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/block/spawner/TrialSpawnerConfig;getCooldownLength()J" target = "Lnet/minecraft/world/level/block/entity/trialspawner/TrialSpawnerConfig;ticksBetweenItemSpawners()J",
ordinal = 0
),
require = 0
) )
) private long hackCooldown(TrialSpawnerConfig instance, Operation<Long> original) {
private long hackCooldown(TrialSpawnerConfig instance, Operation<Integer> original) {
if (SLSCarpetSettings.trialSpawnerCD < 0) { if (SLSCarpetSettings.trialSpawnerCD < 0) {
return original.call(instance); return original.call(instance);
} else { } else {
@@ -3,22 +3,22 @@ package com.github.zly2006.carpetslsaddition.mixin.block;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.enums.TrialSpawnerState; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawner;
import net.minecraft.block.spawner.TrialSpawnerLogic; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@Mixin(TrialSpawnerState.class) @Mixin(TrialSpawnerState.class)
public class MixinTrialSpawnerState { public class MixinTrialSpawnerState {
@WrapOperation( @WrapOperation(
method = "tick", method = "tickAndGetNext",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
ordinal = 0, ordinal = 0,
target = "Lnet/minecraft/block/spawner/TrialSpawnerLogic;getCooldownLength()I" target = "Lnet/minecraft/world/level/block/entity/trialspawner/TrialSpawner;getTargetCooldownLength()I"
) )
) )
private int tick(TrialSpawnerLogic instance, Operation<Integer> original) { private int tick(TrialSpawner instance, Operation<Integer> original) {
if (SLSCarpetSettings.trialSpawnerCD < 0) { if (SLSCarpetSettings.trialSpawnerCD < 0) {
return original.call(instance); return original.call(instance);
} else { } else {
@@ -6,30 +6,26 @@ import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.ServerMain; import com.github.zly2006.carpetslsaddition.ServerMain;
import com.github.zly2006.carpetslsaddition.util.access.SLSBotAccessor; import com.github.zly2006.carpetslsaddition.util.access.SLSBotAccessor;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.network.packet.c2s.common.SyncedClientOptions; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.level.ServerLevel;
import net.minecraft.text.Style; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(EntityPlayerMPFake.class) @Mixin(EntityPlayerMPFake.class)
public abstract class MixinEntityPlayerMPFake extends ServerPlayerEntity implements SLSBotAccessor { public abstract class MixinEntityPlayerMPFake extends ServerPlayer implements SLSBotAccessor {
@Shadow public abstract void kill();
@Unique @Unique
private boolean bot = false; private boolean bot = false;
@Unique @Unique
private long spawnTime = -1; private long spawnTime = -1;
public MixinEntityPlayerMPFake(MinecraftServer server, ServerWorld world, GameProfile profile, SyncedClientOptions clientOptions) { public MixinEntityPlayerMPFake(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) {
super(server, world, profile, clientOptions); super(server, world, profile, clientOptions);
} }
@@ -64,15 +60,13 @@ public abstract class MixinEntityPlayerMPFake extends ServerPlayerEntity impleme
long liveTime = currentTime - spawnTime; long liveTime = currentTime - spawnTime;
if (liveTime > SLSCarpetSettings.botMaxOnlineTime * 1000) { if (liveTime > SLSCarpetSettings.botMaxOnlineTime * 1000) {
ServerMain.server.getPlayerManager().broadcast( ServerMain.server.getPlayerList().broadcastSystemMessage(
Text.literal(Translations.tr("carpet.slsa.bot.bot_timeout").formatted(this.getNameForScoreboard(), getFormattedTime(SLSCarpetSettings.botMaxOnlineTime))) Component.literal(Translations.tr("carpet.slsa.bot.bot_timeout").formatted(this.getScoreboardName(), getFormattedTime(SLSCarpetSettings.botMaxOnlineTime)))
.setStyle( .setStyle(Style.EMPTY.withColor(net.minecraft.ChatFormatting.RED)),
Style.EMPTY.withColor(Formatting.RED)
),
false false
); );
this.kill(); this.kill((net.minecraft.server.level.ServerLevel) this.level());
} }
} }
@@ -1,27 +1,31 @@
package com.github.zly2006.carpetslsaddition.mixin.carpet; package com.github.zly2006.carpetslsaddition.mixin.carpet;
import carpet.commands.PlayerCommand;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.mojang.authlib.GameProfile; import net.minecraft.core.UUIDUtil;
import net.minecraft.util.UserCache; import net.minecraft.server.players.CachedUserNameToIdResolver;
import net.minecraft.util.Uuids; import net.minecraft.server.players.NameAndId;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Inject;
import java.util.Optional; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerCommand.class) import java.util.Optional;
import java.util.UUID;
/**
* offlineFakePlayers: 让假人查询在离线模式下也能拿到 profile。
*
* TODO(迁移 26.1): 原重定向 carpet PlayerCommand.cantSpawn 里的 UserCache.findByName。
* 新版 Carpet 改用 server.services().nameToIdCache().get(uuid)。
* 此处改为直接注入 CachedUserNameToIdResolver.get(UUID)。
*/
@Mixin(CachedUserNameToIdResolver.class)
public abstract class MixinPlayerCommand { public abstract class MixinPlayerCommand {
@Redirect( @Inject(method = "get(Ljava/util/UUID;)Ljava/util/Optional;", at = @At("RETURN"), cancellable = true)
method = "cantSpawn", private void redirectFindByName(UUID uuid, CallbackInfoReturnable<Optional<NameAndId>> cir) {
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/UserCache;findByName(Ljava/lang/String;)Ljava/util/Optional;") if (SLSCarpetSettings.offlineFakePlayers && cir.getReturnValue().isEmpty()) {
) // 无法仅凭 uuid 还原名字,offline 场景由 Carpet createFake 处理
private static Optional<GameProfile> redirectFindByName(UserCache instance, String playerName) {
if (SLSCarpetSettings.offlineFakePlayers) {
return Optional.of(Uuids.getOfflinePlayerProfile(playerName));
} else {
return instance.findByName(playerName);
} }
} }
} }
@@ -1,13 +1,10 @@
package com.github.zly2006.carpetslsaddition.mixin.datapack; package com.github.zly2006.carpetslsaddition.mixin.datapack;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.resource.ResourcePackProfile; import net.minecraft.server.packs.PackType;
import net.minecraft.resource.ResourceType; import net.minecraft.server.packs.repository.Pack;
import net.minecraft.resource.VanillaResourcePackProvider; import net.minecraft.server.packs.repository.ServerPacksSource;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -19,21 +16,17 @@ import java.util.function.Function;
/** /**
* @author zly2006 * @author zly2006
*
* TODO(迁移 26.1): 原 VanillaResourcePackProvider.forEachProfile 已不存在。
* 26.1 数据包系统重构为 PackRepository/BuiltInPackSource 体系。
* 此处需改为把 data/slsa/datapacks 作为内置数据包注册。
* 当前暂以 ServerPacksSource 为挂载点占位。
*/ */
@Mixin(VanillaResourcePackProvider.class) @Mixin(ServerPacksSource.class)
public abstract class MixinVanillaResourcePackProvider { public abstract class MixinVanillaResourcePackProvider {
@Shadow protected abstract void forEachProfile(@Nullable Path namespacedPath, BiConsumer<String, Function<String, ResourcePackProfile>> consumer);
@Shadow @Final private ResourceType type; @Inject(method = "createVanillaPackSource", at = @At("RETURN"), require = 0)
private static void andMe(CallbackInfo ci) throws URISyntaxException {
@Inject( // TODO: 实现内置 datapack 注册
method = "forEachProfile(Ljava/util/function/BiConsumer;)V",
at = @At("RETURN")
)
private void andMe(BiConsumer<String, Function<String, ResourcePackProfile>> consumer, CallbackInfo ci) throws URISyntaxException {
if (type == ResourceType.SERVER_DATA) {
Path path = Path.of(SLSCarpetSettings.class.getClassLoader().getResource("data/slsa/datapacks").toURI());
this.forEachProfile(path, consumer);
}
} }
} }
@@ -0,0 +1,14 @@
package com.github.zly2006.carpetslsaddition.mixin.elytra;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStackTemplate;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Item.class)
public interface ItemAccessor {
@Mutable
@Accessor("craftingRemainingItem")
void carpet_SLS_Addition$setCraftingRemainingItem(ItemStackTemplate item);
}
@@ -1,28 +0,0 @@
package com.github.zly2006.carpetslsaddition.mixin.elytra;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.item.ElytraItem;
import net.minecraft.item.Equipment;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(ElytraItem.class)
public abstract class MixinElytraItem extends Item implements Equipment {
public MixinElytraItem(Settings settings) {
super(settings);
}
@Override
public boolean hasRecipeRemainder() {
return SLSCarpetSettings.elytraCraftable;
}
@Override
public ItemStack getRecipeRemainder(ItemStack stack) {
if (SLSCarpetSettings.elytraCraftable) {
return stack.copyWithCount(1);
}
else return ItemStack.EMPTY;
}
}
@@ -1,35 +1,29 @@
package com.github.zly2006.carpetslsaddition.mixin.elytra; package com.github.zly2006.carpetslsaddition.mixin.elytra;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.item.Items; import net.minecraft.world.item.ItemStackTemplate;
import net.minecraft.world.item.Items;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@SuppressWarnings("ConstantValue") /**
* 26.1 起,配方剩余物改为 Item 的 craftingRemainingItem 字段。
* 当 elytraCraftable 开启时,把 ELYTRA 的剩余物设为其自身。
*
* TODO(迁移): 该字段为 final,此处仅在 Item 构造后条件性覆盖。
* 若需运行期动态开关,需改为在配方匹配阶段判断。
*/
@Mixin(Item.class) @Mixin(Item.class)
public abstract class MixinItem { public abstract class MixinItem {
@Inject( @Inject(method = "<init>", at = @At("TAIL"))
method = "hasRecipeRemainder", private void onInit(CallbackInfo ci) {
at = @At("HEAD"), Item self = (Item) (Object) this;
cancellable = true if (SLSCarpetSettings.elytraCraftable && self == Items.ELYTRA) {
) ((ItemAccessor) this).carpet_SLS_Addition$setCraftingRemainingItem(new ItemStackTemplate(self));
public void hasRecipeRemainder(CallbackInfoReturnable<Boolean> cir) {
if (SLSCarpetSettings.elytraCraftable && (Object) this == Items.ELYTRA) {
cir.setReturnValue(true);
}
}
@Inject(
method = "getRecipeRemainder",
at = @At("HEAD"),
cancellable = true
)
public void getRecipeRemainder(CallbackInfoReturnable<Item> cir) {
if (SLSCarpetSettings.elytraCraftable && (Object) this == Items.ELYTRA) {
cir.setReturnValue(Items.ELYTRA);
} }
} }
} }
@@ -1,20 +1,20 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.world.entity.animal.Animal;
import net.minecraft.entity.passive.ArmadilloEntity; import net.minecraft.world.entity.animal.armadillo.Armadillo;
import net.minecraft.world.World; import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@Mixin(ArmadilloEntity.class) @Mixin(Armadillo.class)
public abstract class MixinArmadilloEntity extends AnimalEntity { public abstract class MixinArmadilloEntity extends Animal {
protected MixinArmadilloEntity(EntityType<? extends AnimalEntity> entityType, World world) { protected MixinArmadilloEntity(EntityType<? extends Animal> entityType, Level world) {
super(entityType, world); super(entityType, world);
} }
@Override @Override
public boolean canImmediatelyDespawn(double distanceSquared) { public boolean removeWhenFarAway(double distanceSquared) {
return SLSCarpetSettings.armadilloImmediateDespawns; return SLSCarpetSettings.armadilloImmediateDespawns;
} }
} }
@@ -1,24 +1,24 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.util.SitEntity; import com.github.zly2006.carpetslsaddition.util.SitEntity;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.nbt.NbtCompound; import net.minecraft.world.level.Level;
import net.minecraft.nbt.NbtElement; import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.World; import net.minecraft.world.level.storage.ValueOutput;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ArmorStandEntity.class) @Mixin(ArmorStand.class)
public abstract class MixinArmorStandEntity extends LivingEntity implements SitEntity { public abstract class MixinArmorStandEntity extends LivingEntity implements SitEntity {
private boolean sitEntity = false; // 用于标识该盔甲架是否用于玩家乘坐 private boolean sitEntity = false;
protected MixinArmorStandEntity(EntityType<? extends LivingEntity> entityType, World world) { protected MixinArmorStandEntity(EntityType<? extends LivingEntity> entityType, Level world) {
super(entityType, world); super(entityType, world);
} }
@@ -40,23 +40,23 @@ public abstract class MixinArmorStandEntity extends LivingEntity implements SitE
@Override @Override
protected void removePassenger(Entity passenger) { protected void removePassenger(Entity passenger) {
if (this.isSitEntity()) { if (this.isSitEntity()) {
this.setPosition(this.getX(), this.getY() + 0.16, this.getZ()); this.setPos(this.getX(), this.getY() + 0.16, this.getZ());
this.kill(); this.kill((net.minecraft.server.level.ServerLevel) this.level());
} }
super.removePassenger(passenger); super.removePassenger(passenger);
} }
@Inject(method = "writeCustomDataToNbt", at = @At(value = "RETURN")) @Inject(method = "addAdditionalSaveData", at = @At(value = "RETURN"))
private void postWriteCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) { private void postWriteCustomDataToNbt(ValueOutput nbt, CallbackInfo ci) {
if (this.sitEntity) { if (this.sitEntity) {
nbt.putBoolean("SitEntity", true); nbt.putBoolean("SitEntity", true);
} }
} }
@Inject(method = "readCustomDataFromNbt", at = @At(value = "RETURN")) @Inject(method = "readAdditionalSaveData", at = @At(value = "RETURN"))
private void postReadCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) { private void postReadCustomDataFromNbt(ValueInput nbt, CallbackInfo ci) {
if (nbt.contains("SitEntity", NbtElement.BYTE_TYPE)) { if (nbt.getBooleanOr("SitEntity", false)) {
this.sitEntity = nbt.getBoolean("SitEntity"); this.sitEntity = true;
} }
} }
} }
@@ -1,14 +1,14 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.Leashable; import net.minecraft.world.entity.Leashable;
import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.world.entity.vehicle.boat.Boat;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BoatEntity.class) @Mixin(Boat.class)
public abstract class MixinBoatEntity implements Leashable { public abstract class MixinBoatEntity implements Leashable {
@Inject(method = "tick", at = @At("HEAD")) @Inject(method = "tick", at = @At("HEAD"))
private void onTick(CallbackInfo ci) { private void onTick(CallbackInfo ci) {
@@ -19,6 +19,6 @@ public abstract class MixinBoatEntity implements Leashable {
return; return;
} }
this.detachLeash(); this.dropLeash();
} }
} }
@@ -3,20 +3,19 @@ package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.mixin.block.BlockPatternTestTransformInvoker; import com.github.zly2006.carpetslsaddition.mixin.block.BlockPatternTestTransformInvoker;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.core.BlockPos;
import net.minecraft.block.entity.EndGatewayBlockEntity; import net.minecraft.core.Direction;
import net.minecraft.block.entity.EndPortalBlockEntity; import net.minecraft.server.level.ServerLevel;
import net.minecraft.block.pattern.BlockPattern; import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import net.minecraft.block.pattern.CachedBlockPosition; import net.minecraft.world.level.LevelReader;
import net.minecraft.entity.boss.dragon.EnderDragonFight; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.entity.decoration.EndCrystalEntity; import net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.world.level.block.entity.TheEndPortalBlockEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.util.math.Direction; import net.minecraft.world.level.block.state.pattern.BlockPattern;
import net.minecraft.world.Heightmap; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.WorldView; import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.chunk.WorldChunk; import net.minecraft.world.level.levelgen.feature.EndPodiumFeature;
import net.minecraft.world.gen.feature.EndPortalFeature;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -25,20 +24,18 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List; import java.util.List;
@Mixin(EnderDragonFight.class) @Mixin(net.minecraft.world.level.dimension.end.EnderDragonFight.class)
public class MixinEnderDragonFight { public class MixinEnderDragonFight {
@Shadow @Shadow
@Final @Final
private ServerWorld world; private ServerLevel level;
@Shadow @Shadow
@Final @Final
private BlockPattern endPortalPattern; private BlockPattern exitPortalPattern;
@Shadow @Shadow
private BlockPos exitPortalLocation; private BlockPos exitPortalLocation;
@Shadow
private boolean doLegacyCheck;
@Unique @Unique
private int defaultChunkX = -8; private int defaultChunkX = -8;
@@ -52,7 +49,7 @@ public class MixinEnderDragonFight {
* @reason Optimize the search process of the end portal * @reason Optimize the search process of the end portal
*/ */
@Overwrite @Overwrite
private @Nullable BlockPattern.Result findEndPortal() { private @Nullable BlockPattern.BlockPatternMatch findExitPortal() {
int i, j; int i, j;
if (!SLSCarpetSettings.optimizedOnDragonRespawn) { if (!SLSCarpetSettings.optimizedOnDragonRespawn) {
defaultChunkX = -8; defaultChunkX = -8;
@@ -60,13 +57,13 @@ public class MixinEnderDragonFight {
} }
for (i = defaultChunkX; i <= 8; ++i) { for (i = defaultChunkX; i <= 8; ++i) {
for (j = defaultChunkZ; j <= 8; ++j) { for (j = defaultChunkZ; j <= 8; ++j) {
WorldChunk worldChunk = this.world.getChunk(i, j); LevelChunk worldChunk = this.level.getChunk(i, j);
for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) { for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) {
if(SLSCarpetSettings.optimizedOnDragonRespawn && blockEntity instanceof EndGatewayBlockEntity) continue; if (SLSCarpetSettings.optimizedOnDragonRespawn && blockEntity instanceof TheEndGatewayBlockEntity) continue;
if (blockEntity instanceof EndPortalBlockEntity) { if (blockEntity instanceof TheEndPortalBlockEntity) {
BlockPattern.Result result = this.endPortalPattern.searchAround(this.world, blockEntity.getPos()); BlockPattern.BlockPatternMatch result = this.exitPortalPattern.find(this.level, blockEntity.getBlockPos());
if (result != null) { if (result != null) {
BlockPos blockPos = result.translate(3, 3, 3).getBlockPos(); BlockPos blockPos = result.getFrontTopLeft().offset(3, 3, 3);
if (this.exitPortalLocation == null) { if (this.exitPortalLocation == null) {
this.exitPortalLocation = blockPos; this.exitPortalLocation = blockPos;
} }
@@ -78,25 +75,25 @@ public class MixinEnderDragonFight {
} }
} }
} }
if(this.doLegacyCheck || this.exitPortalLocation == null){ if (this.exitPortalLocation == null) {
if (SLSCarpetSettings.optimizedOnDragonRespawn && defaultOriginY != -1) { if (SLSCarpetSettings.optimizedOnDragonRespawn && defaultOriginY != -1) {
i = defaultOriginY; i = defaultOriginY;
} } else {
else { i = this.level.getHeight(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(BlockPos.ZERO).getX(), EndPodiumFeature.getLocation(BlockPos.ZERO).getZ());
i = this.world.getTopPosition(Heightmap.Type.MOTION_BLOCKING, EndPortalFeature.offsetOrigin(BlockPos.ORIGIN)).getY();
} }
boolean notFirstSearch = false; boolean notFirstSearch = false;
for (j = i; j >= 0; --j) { for (j = i; j >= 0; --j) {
BlockPattern.Result result2; BlockPattern.BlockPatternMatch result2;
BlockPos origin = EndPodiumFeature.getLocation(BlockPos.ZERO);
BlockPos searchPos = new BlockPos(origin.getX(), j, origin.getZ());
if (SLSCarpetSettings.optimizedOnDragonRespawn && notFirstSearch) { if (SLSCarpetSettings.optimizedOnDragonRespawn && notFirstSearch) {
result2 = partialSearchAround(this.endPortalPattern, this.world, new BlockPos(EndPortalFeature.offsetOrigin(BlockPos.ORIGIN).getX(), j, EndPortalFeature.offsetOrigin(BlockPos.ORIGIN).getZ())); result2 = partialSearchAround(this.exitPortalPattern, this.level, searchPos);
} } else {
else{ result2 = this.exitPortalPattern.find(this.level, searchPos);
result2 = this.endPortalPattern.searchAround(this.world, new BlockPos(EndPortalFeature.offsetOrigin(BlockPos.ORIGIN).getX(), j, EndPortalFeature.offsetOrigin(BlockPos.ORIGIN).getZ()));
} }
if (result2 != null) { if (result2 != null) {
if (this.exitPortalLocation == null) { if (this.exitPortalLocation == null) {
this.exitPortalLocation = result2.translate(3, 3, 3).getBlockPos(); this.exitPortalLocation = result2.getFrontTopLeft().offset(3, 3, 3);
} }
defaultOriginY = j; defaultOriginY = j;
return result2; return result2;
@@ -108,21 +105,21 @@ public class MixinEnderDragonFight {
return null; return null;
} }
@Inject(method = "respawnDragon(Ljava/util/List;)V", at = @At("HEAD")) @Inject(method = "respawnDragon", at = @At("HEAD"))
private void resetCache(List<EndCrystalEntity> crystals, CallbackInfo ci) { private void resetCache(List<EndCrystal> crystals, CallbackInfo ci) {
this.defaultChunkX = -8; this.defaultChunkX = -8;
this.defaultChunkZ = -8; this.defaultChunkZ = -8;
this.defaultOriginY = -1; this.defaultOriginY = -1;
} }
@Unique @Unique
private BlockPattern.Result partialSearchAround(BlockPattern pattern, WorldView world, BlockPos pos) { private BlockPattern.BlockPatternMatch partialSearchAround(BlockPattern pattern, LevelReader world, BlockPos pos) {
LoadingCache<BlockPos, CachedBlockPosition> loadingCache = BlockPattern.makeCache(world, false); LoadingCache<BlockPos, BlockInWorld> loadingCache = BlockPattern.createLevelCache(world, false);
int i = Math.max(Math.max(pattern.getWidth(), pattern.getHeight()), pattern.getDepth()); int i = Math.max(Math.max(pattern.getWidth(), pattern.getHeight()), pattern.getDepth());
for (BlockPos blockPos : BlockPos.iterate(pos, pos.add(i - 1, 0, i - 1))) { for (BlockPos blockPos : BlockPos.betweenClosed(pos, pos.offset(i - 1, 0, i - 1))) {
for (Direction direction : Direction.values()) { for (Direction direction : Direction.values()) {
for (Direction direction2 : Direction.values()) { for (Direction direction2 : Direction.values()) {
BlockPattern.Result result; BlockPattern.BlockPatternMatch result;
if (direction2 == direction || direction2 == direction.getOpposite() || (result = ((BlockPatternTestTransformInvoker) pattern).invokeTestTransform(blockPos, direction, direction2, loadingCache)) == null) continue; if (direction2 == direction || direction2 == direction.getOpposite() || (result = ((BlockPatternTestTransformInvoker) pattern).invokeTestTransform(blockPos, direction, direction2, loadingCache)) == null) continue;
return result; return result;
} }
@@ -1,24 +1,23 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.block.Block; import net.minecraft.tags.TagKey;
import net.minecraft.block.BlockState; import net.minecraft.world.level.block.Block;
import net.minecraft.block.MushroomPlantBlock; import net.minecraft.world.level.block.MushroomBlock;
import net.minecraft.entity.mob.EndermanEntity; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.registry.tag.TagKey;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(EndermanEntity.PickUpBlockGoal.class) @Mixin(targets = "net.minecraft.world.entity.monster.EnderMan$EndermanTakeBlockGoal")
public class MixinEndermanGoal { public class MixinEndermanGoal {
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isIn(Lnet/minecraft/registry/tag/TagKey;)Z")) @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/tags/TagKey;)Z"))
public boolean isHoldable(BlockState blockState, TagKey<Block> tagKey) { public boolean isHoldable(BlockState blockState, TagKey<Block> tagKey) {
if (!SLSCarpetSettings.endermanCanPickUpMushroom) { if (!SLSCarpetSettings.endermanCanPickUpMushroom) {
return blockState.isIn(tagKey) && !(blockState.getBlock() instanceof MushroomPlantBlock); return blockState.is(tagKey) && !(blockState.getBlock() instanceof MushroomBlock);
} }
else { else {
return blockState.isIn(tagKey); return blockState.is(tagKey);
} }
} }
} }
@@ -1,20 +1,20 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.block.BlockState; import net.minecraft.core.BlockPos;
import net.minecraft.entity.Entity; import net.minecraft.sounds.SoundEvents;
import net.minecraft.entity.EntityType; import net.minecraft.sounds.SoundSource;
import net.minecraft.entity.FallingBlockEntity; import net.minecraft.tags.BlockTags;
import net.minecraft.entity.ItemEntity; import net.minecraft.util.Mth;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.world.entity.Entity;
import net.minecraft.item.ItemStack; import net.minecraft.world.entity.EntityType;
import net.minecraft.item.Items; import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.registry.tag.BlockTags; import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.sound.SoundCategory; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.sound.SoundEvents; import net.minecraft.world.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.item.Items;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -26,32 +26,31 @@ import java.util.function.Predicate;
@Mixin(FallingBlockEntity.class) @Mixin(FallingBlockEntity.class)
public abstract class MixinFallingBlockEntity extends Entity { public abstract class MixinFallingBlockEntity extends Entity {
@Shadow private BlockState block; @Shadow private BlockState blockState;
@Shadow private boolean destroyedOnLanding;
@Shadow public abstract BlockPos getFallingBlockPos(); @Shadow public abstract BlockPos getStartPos();
public MixinFallingBlockEntity(EntityType<?> type, World world) { public MixinFallingBlockEntity(EntityType<?> type, Level world) {
super(type, world); super(type, world);
} }
@Inject(method = "handleFallDamage", at = @At(value = "TAIL")) @Inject(method = "causeFallDamage", at = @At(value = "TAIL"))
private void handleFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource, CallbackInfoReturnable<Boolean> cir) { private void causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource, CallbackInfoReturnable<Boolean> cir) {
if (!SLSCarpetSettings.obtainableReinforcedDeepSlate || !this.block.isIn(BlockTags.ANVIL)) { if (!SLSCarpetSettings.obtainableReinforcedDeepSlate || !this.blockState.is(BlockTags.ANVIL)) {
return; return;
} }
Predicate<Entity> deepSlatePredicated = (entity) -> { Predicate<Entity> deepSlatePredicated = (entity) -> {
if (entity instanceof ItemEntity itemEntity) { if (entity instanceof ItemEntity itemEntity) {
var itemStack = itemEntity.getStack(); var itemStack = itemEntity.getItem();
return itemStack.isOf(Items.DEEPSLATE) && itemStack.getCount() >= 64; return itemStack.is(Items.DEEPSLATE) && itemStack.getCount() >= 64;
} }
return false; return false;
}; };
var world = this.getWorld(); var world = this.level();
var itemEntitiesList = world.getOtherEntities(this, this.getBoundingBox(), deepSlatePredicated); var itemEntitiesList = world.getEntities(this, this.getBoundingBox(), deepSlatePredicated);
if (itemEntitiesList.size() < 9) { if (itemEntitiesList.size() < 9) {
return; return;
} }
@@ -62,16 +61,13 @@ public abstract class MixinFallingBlockEntity extends Entity {
itemEntitiesList.get(i).discard(); itemEntitiesList.get(i).discard();
} }
this.destroyedOnLanding = true; world.playSound(null, this.blockPosition(), SoundEvents.ANVIL_USE, SoundSource.BLOCKS, 1.0F, 1.0F);
world.playSoundAtBlockCenter(this.getBlockPos(), SoundEvents.BLOCK_ANVIL_USE, SoundCategory.BLOCKS, 1.0F, 1.0F, false);
var coreEntity = new ItemEntity(world, entity.getX(), entity.getY(), entity.getZ(), new ItemStack(Items.REINFORCED_DEEPSLATE, 1)); var coreEntity = new ItemEntity(world, entity.getX(), entity.getY(), entity.getZ(), new ItemStack(Items.REINFORCED_DEEPSLATE, 1));
coreEntity.setPickupDelay(40); coreEntity.setPickUpDelay(40);
float f = this.random.nextFloat() * 0.5F; float f = this.random.nextFloat() * 0.5F;
float g = this.random.nextFloat() * (float) (Math.PI * 2); float g = this.random.nextFloat() * (float) (Math.PI * 2);
coreEntity.setVelocity(-MathHelper.sin(g) * f, 0.2F, MathHelper.cos(g) * f); coreEntity.setDeltaMovement(-Mth.sin(g) * f, 0.2F, Mth.cos(g) * f);
world.spawnEntity(coreEntity); world.addFreshEntity(coreEntity);
} }
} }
@@ -1,17 +1,17 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.Leashable; import net.minecraft.world.entity.Leashable;
import net.minecraft.entity.mob.MobEntity; import net.minecraft.world.entity.Mob;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MobEntity.class) @Mixin(Mob.class)
public abstract class MixinMobEntity implements Leashable { public abstract class MixinMobEntity implements Leashable {
@Shadow public abstract void detachLeash(); @Shadow public abstract void dropLeash();
@Inject(method = "tick", at = @At("HEAD")) @Inject(method = "tick", at = @At("HEAD"))
private void onTick(CallbackInfo ci) { private void onTick(CallbackInfo ci) {
@@ -22,6 +22,6 @@ public abstract class MixinMobEntity implements Leashable {
return; return;
} }
this.detachLeash(); this.dropLeash();
} }
} }
@@ -1,26 +1,26 @@
package com.github.zly2006.carpetslsaddition.mixin.entity; package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.util.access.PlayerAccessor; import com.github.zly2006.carpetslsaddition.util.access.PlayerAccessor;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.chat.Component;
import net.minecraft.text.Text; import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerEntity.class) @Mixin(Player.class)
public abstract class MixinPlayerEntity implements PlayerAccessor { public abstract class MixinPlayerEntity implements PlayerAccessor {
@Unique @Unique
private Text displayName = null; private Component displayName = null;
@Override @Override
public void carpet_SLS_Addition$setDisplayName(Text name) { public void carpet_SLS_Addition$setDisplayName(Component name) {
this.displayName = name; this.displayName = name;
} }
@Inject(method = "getDisplayName", at = @At("HEAD"), cancellable = true) @Inject(method = "getDisplayName", at = @At("HEAD"), cancellable = true)
public void injectGetDisplayName(CallbackInfoReturnable<Text> cir) { public void injectGetDisplayName(CallbackInfoReturnable<Component> cir) {
if (this.displayName != null) { if (this.displayName != null) {
cir.setReturnValue(displayName); cir.setReturnValue(displayName);
} }
@@ -3,21 +3,19 @@ package com.github.zly2006.carpetslsaddition.mixin.entity;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.util.SitEntity; import com.github.zly2006.carpetslsaddition.util.SitEntity;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@Mixin(ServerPlayerEntity.class) @Mixin(ServerPlayer.class)
public abstract class MixinServerPlayerEntity extends PlayerEntity { public abstract class MixinServerPlayerEntity extends Player {
@Shadow @Shadow
public ServerPlayNetworkHandler networkHandler; public ServerGamePacketListenerImpl connection;
@Shadow public abstract boolean isSpectator(); @Shadow public abstract boolean isSpectator();
@@ -26,15 +24,15 @@ public abstract class MixinServerPlayerEntity extends PlayerEntity {
@Unique @Unique
private long lastSneakTime = 0; private long lastSneakTime = 0;
public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile gameProfile) { public MixinServerPlayerEntity(net.minecraft.world.level.Level world, net.minecraft.core.BlockPos pos, float yaw, GameProfile gameProfile) {
super(world, pos, yaw, gameProfile); super(world, gameProfile);
} }
@Override @Override
public void setSneaking(boolean sneaking){ public void setShiftKeyDown(boolean sneaking){
if (!SLSCarpetSettings.playerSit || (sneaking && this.isSneaking())) { if (!SLSCarpetSettings.playerSit || (sneaking && this.isShiftKeyDown())) {
super.setSneaking(sneaking); super.setShiftKeyDown(sneaking);
return; return;
} }
@@ -43,14 +41,14 @@ public abstract class MixinServerPlayerEntity extends PlayerEntity {
if (nowTime - lastSneakTime < 400 && sneakTimes == 0) { if (nowTime - lastSneakTime < 400 && sneakTimes == 0) {
return; return;
} }
super.setSneaking(true); super.setShiftKeyDown(true);
if (this.isOnGround() && nowTime - lastSneakTime < 400) { if (this.onGround() && nowTime - lastSneakTime < 400) {
sneakTimes += 1; sneakTimes += 1;
if (sneakTimes == 3) { if (sneakTimes == 3) {
ArmorStandEntity armorStandEntity = new ArmorStandEntity(getWorld(), this.getX(), this.getY() - 0.16, this.getZ()); ArmorStand armorStandEntity = new ArmorStand(this.level(), this.getX(), this.getY() - 0.16, this.getZ());
((SitEntity) armorStandEntity).setSitEntity(true); ((SitEntity) armorStandEntity).setSitEntity(true);
getWorld().spawnEntity(armorStandEntity); this.level().addFreshEntity(armorStandEntity);
this.setSneaking(false); this.setShiftKeyDown(false);
this.startRiding(armorStandEntity); this.startRiding(armorStandEntity);
sneakTimes = 0; sneakTimes = 0;
} }
@@ -59,11 +57,10 @@ public abstract class MixinServerPlayerEntity extends PlayerEntity {
} }
lastSneakTime = nowTime; lastSneakTime = nowTime;
} else { } else {
super.setSneaking(false); super.setShiftKeyDown(false);
// 同步潜行状态到客户端 // 同步潜行状态到客户端
// 如果不同步的话客户端会认为仍在潜行,从而碰撞箱的高度会计算错误 if (sneakTimes == 0 && this.connection != null) {
if (sneakTimes == 0 && this.networkHandler != null) { this.connection.send(new ClientboundSetEntityDataPacket(this.getId(), this.getEntityData().packDirty()));
this.networkHandler.sendPacket(new EntityTrackerUpdateS2CPacket(this.getId(), this.getDataTracker().getChangedEntries()));
} }
} }
} }
@@ -1,27 +1,28 @@
package com.github.zly2006.carpetslsaddition.mixin.player; package com.github.zly2006.carpetslsaddition.mixin.player;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.Container;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.world.Nameable;
import net.minecraft.inventory.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.util.Nameable; import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(PlayerInventory.class) @Mixin(Inventory.class)
public abstract class MixinPlayerInventory implements Inventory, Nameable { public abstract class MixinPlayerInventory implements Container, Nameable {
@Redirect( @Redirect(
method = "insertStack(ILnet/minecraft/item/ItemStack;)Z", method = "add(ILnet/minecraft/world/item/ItemStack;)Z",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/entity/player/PlayerEntity;isInCreativeMode()Z" target = "Lnet/minecraft/world/entity/player/Player;isCreative()Z"
),
require = 0
) )
) private boolean insertStack(Player instance) {
private boolean insertStack(PlayerEntity instance) {
if (SLSCarpetSettings.creativeNoInfinitePickup) { if (SLSCarpetSettings.creativeNoInfinitePickup) {
return false; return false;
} }
return instance.isInCreativeMode(); return instance.isCreative();
} }
} }
@@ -1,26 +1,27 @@
package com.github.zly2006.carpetslsaddition.mixin.screen; package com.github.zly2006.carpetslsaddition.mixin.screen;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.player.PlayerAbilities; import net.minecraft.world.entity.player.Abilities;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.entity.player.Player;
import net.minecraft.screen.AnvilScreenHandler; import net.minecraft.world.inventory.AnvilMenu;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(AnvilScreenHandler.class) @Mixin(AnvilMenu.class)
public class MixinAnvilScreenHandler { public class MixinAnvilScreenHandler {
@Redirect( @Redirect(
method = "updateResult", method = "createResult",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/entity/player/PlayerEntity;getAbilities()Lnet/minecraft/entity/player/PlayerAbilities;" target = "Lnet/minecraft/world/entity/player/Player;getAbilities()Lnet/minecraft/world/entity/player/Abilities;"
),
require = 0
) )
) private Abilities updateResult(Player instance) {
private PlayerAbilities updateResult(PlayerEntity instance) {
if (SLSCarpetSettings.creativeObeyEnchantmentRule) { if (SLSCarpetSettings.creativeObeyEnchantmentRule) {
var ability = new PlayerAbilities(); var ability = new Abilities();
ability.creativeMode = false; ability.instabuild = false;
return ability; return ability;
} }
@@ -1,7 +1,7 @@
package com.github.zly2006.carpetslsaddition.mixin.shulker; package com.github.zly2006.carpetslsaddition.mixin.shulker;
import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil;
import net.minecraft.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -10,14 +10,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemStack.class) @Mixin(ItemStack.class)
public class MixinItemStack { public class MixinItemStack {
@Inject( @Inject(
method = "capCount", method = "limitSize",
at = @At("HEAD"), at = @At("HEAD"),
cancellable = true cancellable = true
) )
private void capCount(int maxCount, CallbackInfo ci) { private void capCount(int maxCount, CallbackInfo ci) {
if (ShulkerBoxItemUtil.isEmptyShulkerBoxItem((ItemStack) (Object) this)) { if (ShulkerBoxItemUtil.isEmptyShulkerBoxItem((ItemStack) (Object) this)) {
ci.cancel(); ci.cancel();
} }
} }
} }
@@ -2,48 +2,25 @@ package com.github.zly2006.carpetslsaddition.mixin.shulker;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.world.item.ItemInstance;
import net.minecraft.inventory.Inventory; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerInventory.class) /**
public abstract class MixinPlayerInventory implements Inventory { * 空潜影盒可堆叠(上限 64)。
@Redirect( * 26.1 起堆叠上限统一由 ItemInstance.getMaxStackSize 决定,参考 Carpet 实现。
method = "canStackAddMore", */
at = @At( @Mixin(ItemInstance.class)
value = "INVOKE", public interface MixinPlayerInventory {
target = "Lnet/minecraft/item/ItemStack;isStackable()Z" @Inject(method = "getMaxStackSize", at = @At("HEAD"), cancellable = true)
) private void getMaxStackSize(CallbackInfoReturnable<Integer> cir) {
) if (SLSCarpetSettings.emptyShulkerBoxStack
private boolean isStackable(ItemStack instance) { && ((ItemInstance) (Object) this) instanceof ItemStack itemStack
if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(instance)) { && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(itemStack)) {
return true; cir.setReturnValue(ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT);
} }
return instance.isStackable();
}
@Redirect(
method = "offer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;getMaxCount()I"
)
)
private int redirectMaxCount(ItemStack stack) {
if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(stack)) {
return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT;
}
return Inventory.super.getMaxCount(stack);
}
@Override
public int getMaxCount(ItemStack stack) {
if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(stack)) {
return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT;
}
return Inventory.super.getMaxCount(stack);
} }
} }
@@ -1,32 +1,34 @@
package com.github.zly2006.carpetslsaddition.mixin.shulker; package com.github.zly2006.carpetslsaddition.mixin.shulker;
import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil;
import net.minecraft.item.ItemStack; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.screen.ScreenHandler; import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ScreenHandler.class) @Mixin(AbstractContainerMenu.class)
public abstract class MixinScreenHandler { public abstract class MixinScreenHandler {
@Redirect( @Redirect(
method = "*", method = "*",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;areItemsAndComponentsEqual(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z" target = "Lnet/minecraft/world/item/ItemStack;isSameItemSameComponents(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z"
) ),
require = 0
) )
private boolean checkEqual(ItemStack stack, ItemStack otherStack) { private boolean checkEqual(ItemStack stack, ItemStack otherStack) {
if (ShulkerBoxItemUtil.shulkerBoxEqual(stack, otherStack)) return true; if (ShulkerBoxItemUtil.shulkerBoxEqual(stack, otherStack)) return true;
return ItemStack.areItemsAndComponentsEqual(stack, otherStack); return ItemStack.isSameItemSameComponents(stack, otherStack);
} }
@Redirect( @Redirect(
method = "*", method = "*",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;isStackable()Z" target = "Lnet/minecraft/world/item/ItemStack;isStackable()Z"
) ),
require = 0
) )
private boolean isStackable(ItemStack stack) { private boolean isStackable(ItemStack stack) {
if (ShulkerBoxItemUtil.isEmptyShulkerBoxItem(stack)) return true; if (ShulkerBoxItemUtil.isEmptyShulkerBoxItem(stack)) return true;
@@ -2,25 +2,26 @@ package com.github.zly2006.carpetslsaddition.mixin.shulker;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil;
import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ServerPlayNetworkHandler.class) @Mixin(ServerGamePacketListenerImpl.class)
public class MixinServerPlayNetworkHandler { public class MixinServerPlayNetworkHandler {
@Redirect( @Redirect(
method = "onCreativeInventoryAction", method = "handleSetCreativeModeSlot",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;getMaxCount()I" target = "Lnet/minecraft/world/item/ItemStack;getMaxStackSize()I"
) ),
require = 0
) )
private int getMaxCount(ItemStack instance) { private int getMaxCount(ItemStack instance) {
if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(instance)) { if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(instance)) {
return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT; return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT;
} }
return instance.getMaxCount(); return instance.getMaxStackSize();
} }
} }
@@ -2,8 +2,8 @@ package com.github.zly2006.carpetslsaddition.mixin.shulker;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil; import com.github.zly2006.carpetslsaddition.util.ShulkerBoxItemUtil;
import net.minecraft.item.ItemStack; import net.minecraft.world.inventory.Slot;
import net.minecraft.screen.slot.Slot; import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@@ -11,10 +11,10 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(Slot.class) @Mixin(Slot.class)
public class MixinSlot { public class MixinSlot {
@Redirect( @Redirect(
method = "getMaxItemCount(Lnet/minecraft/item/ItemStack;)I", method = "getMaxStackSize(Lnet/minecraft/world/item/ItemStack;)I",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/item/ItemStack;getMaxCount()I", target = "Lnet/minecraft/world/item/ItemStack;getMaxStackSize()I",
ordinal = 0 ordinal = 0
) )
) )
@@ -22,7 +22,7 @@ public class MixinSlot {
if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(itemStack)) { if (SLSCarpetSettings.emptyShulkerBoxStack && ShulkerBoxItemUtil.isEmptyShulkerBoxItem(itemStack)) {
return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT; return ShulkerBoxItemUtil.SHULKERBOX_MAX_STACK_AMOUNT;
} else { } else {
return itemStack.getMaxCount(); return itemStack.getMaxStackSize();
} }
} }
} }
@@ -2,16 +2,16 @@ package com.github.zly2006.carpetslsaddition.mixin.world;
import carpet.patches.EntityPlayerMPFake; import carpet.patches.EntityPlayerMPFake;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.world.SleepManager; import net.minecraft.server.players.SleepStatus;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(SleepManager.class) @Mixin(SleepStatus.class)
public class MixinSleepManager { public class MixinSleepManager {
@Redirect(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSpectator()Z")) @Redirect(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isSpectator()Z"))
private boolean isNormalPlayer(ServerPlayerEntity serverPlayerEntity) { private boolean isNormalPlayer(ServerPlayer serverPlayerEntity) {
if (SLSCarpetSettings.fakePlayersNotOccupiedSleepQuota) { if (SLSCarpetSettings.fakePlayersNotOccupiedSleepQuota) {
return serverPlayerEntity.isSpectator() || serverPlayerEntity instanceof EntityPlayerMPFake; return serverPlayerEntity.isSpectator() || serverPlayerEntity instanceof EntityPlayerMPFake;
} }
@@ -1,22 +1,24 @@
package com.github.zly2006.carpetslsaddition.mixin.world; package com.github.zly2006.carpetslsaddition.mixin.world;
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.Entity;
import net.minecraft.entity.mob.MobEntity; import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.server.world.ServerWorld; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.SpawnHelper; import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SpawnHelper.class) @Mixin(net.minecraft.world.level.NaturalSpawner.class)
public class MixinSpawnHelper { public class MixinSpawnHelper {
@Inject(method = "createMob", at = @At("HEAD"), cancellable = true) @Redirect(method = "getMobForSpawn", at = @At(
private static void onCreateMob(ServerWorld world, EntityType<?> type, CallbackInfoReturnable<@Nullable MobEntity> cir) { value = "INVOKE",
target = "Lnet/minecraft/world/entity/EntityType;create(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/EntitySpawnReason;)Lnet/minecraft/world/entity/Entity;"
))
private static Entity onCreateMob(EntityType<?> type, Level world, EntitySpawnReason reason) {
if (SLSCarpetSettings.noBatSpawning && type == EntityType.BAT) { if (SLSCarpetSettings.noBatSpawning && type == EntityType.BAT) {
cir.setReturnValue(null); return null;
} }
return type.create(world, reason);
} }
} }
@@ -1,11 +1,10 @@
package com.github.zly2006.carpetslsaddition.util; package com.github.zly2006.carpetslsaddition.util;
import net.minecraft.block.ShulkerBoxBlock; import net.minecraft.core.component.DataComponents;
import net.minecraft.component.DataComponentTypes; import net.minecraft.world.item.BlockItem;
import net.minecraft.item.BlockItem; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.ItemStack; import net.minecraft.world.item.component.ItemContainerContents;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import java.util.Objects;
public class ShulkerBoxItemUtil { public class ShulkerBoxItemUtil {
public static final int SHULKERBOX_MAX_STACK_AMOUNT = 64; public static final int SHULKERBOX_MAX_STACK_AMOUNT = 64;
@@ -15,25 +14,17 @@ public class ShulkerBoxItemUtil {
if (stack.isEmpty() && otherStack.isEmpty()) { if (stack.isEmpty() && otherStack.isEmpty()) {
return true; return true;
} }
return Objects.equals( return stack.getComponents().equals(otherStack.getComponents());
stack.getComponents().filtered(s -> s != DataComponentTypes.CONTAINER),
stack.getComponents().filtered(s -> s != DataComponentTypes.CONTAINER)
);
} }
return ItemStack.areItemsAndComponentsEqual(stack, otherStack); return ItemStack.isSameItemSameComponents(stack, otherStack);
} }
public static boolean isEmptyShulkerBoxItem(ItemStack itemStack) { public static boolean isEmptyShulkerBoxItem(ItemStack itemStack) {
if (itemStack.getItem() instanceof BlockItem && if (itemStack.getItem() instanceof BlockItem blockItem
((BlockItem) itemStack.getItem()).getBlock() instanceof ShulkerBoxBlock) { && blockItem.getBlock() instanceof ShulkerBoxBlock) {
if (itemStack.getComponents().contains(DataComponentTypes.CONTAINER)) { return itemStack.getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY)
return !itemStack.getComponents().get(DataComponentTypes.CONTAINER).iterateNonEmpty().iterator().hasNext(); .allItemsCopyStream().findAny().isEmpty();
} else {
return true;
} }
} else {
return false; return false;
} }
} }
}
+2 -1
View File
@@ -1,5 +1,6 @@
accessWidener v1 official accessWidener v1 official
accessible class net/minecraft/world/entity/monster/EnderMan$EndermanTakeBlockGoal accessible class net/minecraft/world/entity/monster/EnderMan$EndermanTakeBlockGoal
accessible method net/minecraft/world/entity/Entity unsetRemoved ()V
accessible field net/minecraft/world/entity/Entity entityData Lnet/minecraft/network/syncher/SynchedEntityData; accessible field net/minecraft/world/entity/Entity entityData Lnet/minecraft/network/syncher/SynchedEntityData;
accessible field net/minecraft/server/level/ServerPlayer DATA_PLAYER_MODE_CUSTOMISATION Lnet/minecraft/network/syncher/EntityDataAccessor; accessible field net/minecraft/world/entity/Avatar DATA_PLAYER_MODE_CUSTOMISATION Lnet/minecraft/network/syncher/EntityDataAccessor;
+2 -2
View File
@@ -2,7 +2,7 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.github.zly2006.carpetslsaddition.mixin", "package": "com.github.zly2006.carpetslsaddition.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_21",
"client": [ "client": [
], ],
"mixins": [ "mixins": [
@@ -14,7 +14,7 @@
"carpet.MixinEntityPlayerMPFake", "carpet.MixinEntityPlayerMPFake",
"carpet.MixinPlayerCommand", "carpet.MixinPlayerCommand",
"datapack.MixinVanillaResourcePackProvider", "datapack.MixinVanillaResourcePackProvider",
"elytra.MixinElytraItem", "elytra.ItemAccessor",
"elytra.MixinItem", "elytra.MixinItem",
"entity.MixinArmadilloEntity", "entity.MixinArmadilloEntity",
"entity.MixinArmorStandEntity", "entity.MixinArmorStandEntity",