Merge branch '0.2.0' of github.com:tanyaofei/minecraft-fakeplayer into 0.2.0

# Conflicts:
#	fakeplayer-api/src/main/java/io/github/hello09x/fakeplayer/api/action/ActionType.java
#	fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/CommandRegistry.java
#	fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/impl/ConfigCommand.java
#	fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/command/impl/RotationCommand.java
#	fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/listener/RefillListener.java
#	fakeplayer-core/src/main/java/io/github/hello09x/fakeplayer/core/repository/model/Config.java
This commit is contained in:
tanyaofei 2023-11-11 00:53:19 +08:00
commit cda96bd262
41 changed files with 409 additions and 210 deletions

View File

@ -49,8 +49,9 @@ public enum ActionType implements Translatable {
final String translationKey;
@Override
public @NotNull String translationKey() {
return translationKey;
return this.translationKey;
}
}

View File

@ -3,10 +3,9 @@ package io.github.hello09x.fakeplayer.api.action;
import io.github.hello09x.fakeplayer.api.action.impl.*;
import io.github.hello09x.fakeplayer.api.spi.Action;
import io.github.hello09x.fakeplayer.api.spi.ActionTicker;
import io.github.hello09x.fakeplayer.api.spi.VersionSupport;
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@ -18,14 +17,14 @@ public abstract class BaseActionTicker implements ActionTicker {
protected ActionSetting setting;
public BaseActionTicker(@NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
var versionSupport = Objects.requireNonNull(VersionSupport.getInstance());
var bridge = Objects.requireNonNull(NMSBridge.getInstance());
this.setting = setting;
this.action = switch (action) {
case JUMP -> new JumpAction(versionSupport.player(player));
case LOOK_AT_NEAREST_ENTITY -> new LookAtEntityAction(versionSupport.player(player));
case DROP_ITEM -> new DropItemAction(versionSupport.player(player));
case DROP_STACK -> new DropStackAction(versionSupport.player(player));
case DROP_INVENTORY -> new DropInventoryAction(versionSupport.player(player));
case JUMP -> new JumpAction(bridge.player(player));
case LOOK_AT_NEAREST_ENTITY -> new LookAtEntityAction(bridge.player(player));
case DROP_ITEM -> new DropItemAction(bridge.player(player));
case DROP_STACK -> new DropStackAction(bridge.player(player));
case DROP_INVENTORY -> new DropInventoryAction(bridge.player(player));
default -> null;
};
}

View File

@ -11,14 +11,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.ServiceLoader;
public interface VersionSupport {
public interface NMSBridge {
static @Nullable VersionSupport getInstance() {
static @Nullable NMSBridge getInstance() {
return ServiceLoader
.load(VersionSupport.class, VersionSupport.class.getClassLoader())
.load(NMSBridge.class, NMSBridge.class.getClassLoader())
.stream()
.map(ServiceLoader.Provider::get)
.filter(VersionSupport::isSupported)
.filter(NMSBridge::isSupported)
.findAny()
.orElse(null);
}

View File

@ -0,0 +1,32 @@
package io.github.hello09x.fakeplayer.api.spi;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface NMSGamePacketListener {
int MESSAGE_HISTORY_SIZE = 50;
/**
* @return 获取最后一条消息
*/
@Nullable ReceivedMessage getLastMessage();
/**
* 获取最近消息消息
*
* @return 最近消息
*/
@NotNull List<ReceivedMessage> getRecentMessages();
record ReceivedMessage(
int id,
Component content
) {
}
}

View File

@ -15,7 +15,7 @@ public interface NMSNetwork {
* @param player 假人玩家
* @param address 虚拟地址
*/
void bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address);
@NotNull NMSGamePacketListener bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address);
/**
* 绑定一个虚拟的登陆连接

View File

@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
public interface NMSServerPlayer {
/**
* @return 返回 bukkit Player 对象
*/

View File

@ -0,0 +1,35 @@
package io.github.hello09x.fakeplayer.api.utils;
import io.github.hello09x.bedrock.util.LazyInit;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public abstract class ClientboundSystemChatPackets {
private final static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private final static LazyInit<MethodHandle> ADVENTURE$CONTENT = new LazyInit<>();
public static @Nullable Component getAdventureContent(@NotNull Object packet) {
var adventure$content = ADVENTURE$CONTENT.computeIfAbsent(() -> {
try {
return LOOKUP.findVirtual(packet.getClass(), "adventure$content", MethodType.methodType(Component.class));
} catch (Throwable e) {
return null;
}
}, true);
if (adventure$content != null) {
try {
return (Component) adventure$content.invoke(packet);
} catch (Throwable e) {
return null;
}
}
return null;
}
}

View File

@ -33,6 +33,7 @@
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -3,7 +3,7 @@ package io.github.hello09x.fakeplayer.core;
import io.github.hello09x.bedrock.i18n.I18n;
import io.github.hello09x.bedrock.i18n.I18nSupported;
import io.github.hello09x.bedrock.util.RegistrablePlugin;
import io.github.hello09x.fakeplayer.api.spi.VersionSupport;
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
import io.github.hello09x.fakeplayer.core.command.CommandRegistry;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.listener.FakeplayerListener;
@ -23,14 +23,14 @@ public final class Main extends RegistrablePlugin implements I18nSupported {
private static Main instance;
@Getter
private static VersionSupport versionSupport;
private static NMSBridge bridge;
private I18n i18n;
@Override
public void onLoad() {
versionSupport = VersionSupport.getInstance();
if (versionSupport == null) {
bridge = NMSBridge.getInstance();
if (bridge == null) {
throw new ExceptionInInitializerError("Unsupported Minecraft version: " + Bukkit.getMinecraftVersion());
}
this.i18n = new I18n(this, "message/message");

View File

@ -3,6 +3,7 @@ package io.github.hello09x.fakeplayer.core.command;
import dev.jorel.commandapi.CommandPermission;
import io.github.hello09x.bedrock.command.Usage;
import io.github.hello09x.bedrock.i18n.I18n;
import io.github.hello09x.fakeplayer.api.action.ActionSetting;
import io.github.hello09x.fakeplayer.api.action.ActionType;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.command.impl.*;
@ -22,6 +23,7 @@ public class CommandRegistry {
private final static I18n i18n = Main.i18n();
public static void register() {
command("fakeplayer")
.withAliases("fp")
@ -107,21 +109,6 @@ public class CommandRegistry {
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executesPlayer(DistanceCommand.instance::distance),
command("drop")
.withPermission(Permission.drop)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executes(DropCommand.instance.drop()),
command("dropstack")
.withPermission(Permission.dropstack)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executes(DropCommand.instance.dropstack()),
command("dropinv")
.withPermission(Permission.dropinv)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executes(DropCommand.instance.dropinv()),
command("skin")
.withPermission(Permission.skin)
.withRequirement(CommandSupports::hasTarget)
@ -174,30 +161,60 @@ public class CommandRegistry {
.withPermission(Permission.tp)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executesPlayer(TpCommand.instance::tp),
.executesPlayer(TeleportCommand.instance::tp),
command("tphere")
.withPermission(Permission.tphere)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executesPlayer(TpCommand.instance::tphere),
.executesPlayer(TeleportCommand.instance::tphere),
command("tps")
.withPermission(Permission.tps)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(target("name"))
.executesPlayer(TpCommand.instance::tps),
.executesPlayer(TeleportCommand.instance::tps),
command("attack")
.withPermission(Permission.attack)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.ATTACK)),
.withSubcommands(newActionCommands(ActionType.ATTACK))
.executes(ActionCommand.instance.action(ActionType.ATTACK, ActionSetting.once())),
command("mine")
.withPermission(Permission.mine)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.MINE)),
.withSubcommands(newActionCommands(ActionType.MINE))
.executes(ActionCommand.instance.action(ActionType.MINE, ActionSetting.once())),
command("use")
.withPermission(Permission.use)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.USE)),
.withSubcommands(newActionCommands(ActionType.USE))
.executes(ActionCommand.instance.action(ActionType.USE, ActionSetting.once())),
command("jump")
.withPermission(Permission.jump)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.JUMP))
.executes(ActionCommand.instance.action(ActionType.JUMP, ActionSetting.once())),
command("drop")
.withPermission(Permission.drop)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.DROP_ITEM))
.executes(ActionCommand.instance.action(ActionType.DROP_ITEM, ActionSetting.once())),
command("dropstack")
.withPermission(Permission.dropstack)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.DROP_STACK))
.executes(ActionCommand.instance.action(ActionType.DROP_STACK, ActionSetting.once())),
command("dropinv")
.withPermission(Permission.dropinv)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.DROP_INVENTORY))
.executes(ActionCommand.instance.action(ActionType.DROP_INVENTORY, ActionSetting.once())),
command("sneak")
.withPermission(Permission.sneak)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(
target("name"),
literals("sneaking", List.of("true", "false")))
.executes(SneakCommand.instance::sneak),
command("refill")
.withPermission(RefillCommand.PERMISSION)
.withRequirement(CommandSupports::hasTarget)
@ -206,17 +223,6 @@ public class CommandRegistry {
literals("enabled", List.of("true", "false"))
)
.executes(RefillCommand.instance::refill),
command("jump")
.withPermission(Permission.jump)
.withRequirement(CommandSupports::hasTarget)
.withSubcommands(newActionCommands(ActionType.JUMP)),
command("sneak")
.withPermission(Permission.sneak)
.withRequirement(CommandSupports::hasTarget)
.withOptionalArguments(
literals("sneaking", List.of("true", "false")),
target("name"))
.executes(SneakCommand.instance::sneak),
command("look")
.withPermission(Permission.look)
.withRequirement(CommandSupports::hasTarget)
@ -306,7 +312,8 @@ public class CommandRegistry {
Usage.of("left", i18n.asString("fakeplayer.command.move.left.description")),
Usage.of("right", i18n.asString("fakeplayer.command.move.right.description"))
)
),
)
.executes(MoveCommand.instance.move(1, 0)),
command("ride")
.withPermission(Permission.ride)
@ -364,6 +371,14 @@ public class CommandRegistry {
command("killall")
.withPermission(CommandPermission.OP)
.executes(KillallCommand.instance::killall),
command("msg")
.withPermission(CommandPermission.OP)
.withOptionalArguments(
int32("size", 1),
int32("skip", 0),
target("name")
)
.executes(MsgCommand.instance::msg),
command("reload")
.withPermission(CommandPermission.OP)
.executes(ReloadCommand.instance::reload)

View File

@ -4,6 +4,7 @@ import dev.jorel.commandapi.CommandAPI;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import dev.jorel.commandapi.wrappers.CommandResult;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.core.command.Permission;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@ -11,8 +12,12 @@ import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Optional;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.Component.textOfChildren;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CmdCommand extends AbstractCommand {
@ -28,15 +33,28 @@ public class CmdCommand extends AbstractCommand {
throw CommandAPI.failWithString(i18n.asString("fakeplayer.command.cmd.error.no-permission"));
}
if (name.equals("fakeplayer") || name.equals("fp")) {
if (!sender.isOp() && (name.equals("fakeplayer") || name.equals("fp"))) {
throw CommandAPI.failWithString(i18n.asString("fakeplayer.command.cmd.error.no-permission"));
}
var messageId = Optional.ofNullable(fakeplayerManager.getLastMessage(target))
.map(NMSGamePacketListener.ReceivedMessage::id)
.orElse(-1);
if (!command.execute(target)) {
throw CommandAPI.failWithString(i18n.asString("fakeplayer.command.cmd.error.execute-failed"));
}
sender.sendMessage(i18n.translate("fakeplayer.command.cmd.success.execute", GRAY));
var message = fakeplayerManager.getLastMessage(target);
if (message != null && message.id() != messageId) {
sender.sendMessage(textOfChildren(
text(">> ", GRAY),
text(target.getName(), WHITE),
text(": ", GRAY),
message.content()
));
} else {
sender.sendMessage(i18n.translate("fakeplayer.command.cmd.success.execute", GRAY));
}
}
}

View File

@ -58,7 +58,7 @@ public class ConfigCommand extends AbstractCommand {
manager.setConfig(sender, config, value);
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.config.set.success") + "</gray>",
Placeholder.component("option", i18n.translate(config, GOLD)),
Placeholder.component("option", i18n.translate(config.translationKey(), GOLD)),
Placeholder.component("value", text(value.toString(), WHITE))
));
}

View File

@ -21,10 +21,10 @@ public class DistanceCommand extends AbstractCommand {
public final static DistanceCommand instance = new DistanceCommand();
public void distance(
@NotNull Player sender,
@NotNull CommandArguments args
) throws WrapperCommandSyntaxException {
/**
* 查看距离
*/
public void distance(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = super.getTarget(sender, args);
var from = target.getLocation().toBlockLocation();
var to = sender.getLocation().toBlockLocation();

View File

@ -1,27 +0,0 @@
package io.github.hello09x.fakeplayer.core.command.impl;
import dev.jorel.commandapi.executors.CommandExecutor;
import io.github.hello09x.fakeplayer.api.action.ActionSetting;
import io.github.hello09x.fakeplayer.api.action.ActionType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DropCommand extends AbstractCommand {
public final static DropCommand instance = new DropCommand();
public @NotNull CommandExecutor drop() {
return ActionCommand.instance.action(ActionType.DROP_ITEM, ActionSetting.once());
}
public @NotNull CommandExecutor dropstack() {
return ActionCommand.instance.action(ActionType.DROP_STACK, ActionSetting.once());
}
public @NotNull CommandExecutor dropinv() {
return ActionCommand.instance.action(ActionType.DROP_INVENTORY, ActionSetting.once());
}
}

View File

@ -15,6 +15,9 @@ public class InvseeCommand extends AbstractCommand {
public final static InvseeCommand instance = new InvseeCommand();
/**
* 查看背包
*/
public void invsee(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = super.getTarget(sender, args);
if (!Objects.equals(sender.getLocation().getWorld(), target.getLocation().getWorld())) {

View File

@ -4,10 +4,6 @@ import dev.jorel.commandapi.executors.CommandExecutor;
import io.github.hello09x.fakeplayer.core.Main;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MoveCommand extends AbstractCommand {
@ -20,7 +16,7 @@ public class MoveCommand extends AbstractCommand {
public CommandExecutor move(float forward, float strafing) {
return (sender, args) -> {
var target = getTarget(sender, args);
var handle = Main.getVersionSupport().player(target);
var handle = Main.getBridge().player(target);
float vel = target.isSneaking() ? 0.3F : 1.0F;
if (forward != 0.0F) {
handle.setZza(vel * forward);
@ -28,10 +24,6 @@ public class MoveCommand extends AbstractCommand {
if (strafing != 0.0F) {
handle.setXxa(vel * strafing);
}
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.move.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE))
));
};
}

View File

@ -0,0 +1,26 @@
package io.github.hello09x.fakeplayer.core.command.impl;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MsgCommand extends AbstractCommand {
public final static MsgCommand instance = new MsgCommand();
public void msg(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = super.getTarget(sender, args);
int skip = (int) args.getOptional("skip").orElse(0);
int size = (int) args.getOptional("size").orElse(10);
var messages = fakeplayerManager.getMessages(target, skip, size);
for (var message : messages) {
sender.sendMessage(message.content());
}
}
}

View File

@ -16,7 +16,7 @@ public class RespawnCommand extends AbstractCommand {
public void respawn(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = super.getTarget(sender, args, Entity::isDead);
Main.getVersionSupport().player(target).respawn();
Main.getBridge().player(target).respawn();
}
}

View File

@ -32,7 +32,7 @@ public class RideCommand extends AbstractCommand {
if (entities.isEmpty()) {
return;
}
Main.getVersionSupport().player(target).startRiding(entities.get(0), true);
Main.getBridge().player(target).startRiding(entities.get(0), true);
}
/**
@ -45,7 +45,7 @@ public class RideCommand extends AbstractCommand {
return;
}
Main.getVersionSupport().player(target).startRiding(entity, true);
Main.getBridge().player(target).startRiding(entity, true);
}
/**
@ -63,7 +63,7 @@ public class RideCommand extends AbstractCommand {
return;
}
Main.getVersionSupport().player(target).startRiding(entity, true);
Main.getBridge().player(target).startRiding(entity, true);
}
/**
@ -84,14 +84,14 @@ public class RideCommand extends AbstractCommand {
return;
}
Main.getVersionSupport().player(target).startRiding(sender, true);
Main.getBridge().player(target).startRiding(sender, true);
}
/**
* 停止骑行
*/
public void stopRiding(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
Main.getVersionSupport().player(getTarget(sender, args)).stopRiding();
Main.getBridge().player(getTarget(sender, args)).stopRiding();
}
}

View File

@ -10,8 +10,6 @@ import io.github.hello09x.fakeplayer.core.util.Mth;
import io.papermc.paper.entity.LookAnchor;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -19,21 +17,12 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RotationCommand extends AbstractCommand {
public final static RotationCommand instance = new RotationCommand();
private static String toLocationString(@NotNull Location location) {
return StringUtils.joinWith(", ",
Mth.floor(location.getX(), 0.5),
Mth.floor(location.getY(), 0.5),
Mth.floor(location.getZ(), 0.5));
}
/**
* 看向给定坐标
*/
@ -42,11 +31,6 @@ public class RotationCommand extends AbstractCommand {
var target = getTarget(sender, args);
var location = Objects.requireNonNull((Location) args.get("location"));
target.lookAt(location, LookAnchor.EYES);
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.look.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE)),
Placeholder.component("direction", text(toLocationString(location), WHITE))
));
}
/**
@ -56,12 +40,6 @@ public class RotationCommand extends AbstractCommand {
return (sender, args) -> {
var target = getTarget(sender, args);
look(target, direction);
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.look.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE)),
Placeholder.component("direction", i18n.translate(direction, WHITE))
));
};
}
@ -86,7 +64,7 @@ public class RotationCommand extends AbstractCommand {
* 看向指定方向
*/
private void look(@NotNull Player player, float yaw, float pitch) {
var handle = Main.getVersionSupport().player(player);
var handle = Main.getBridge().player(player);
handle.setYRot(yaw % 360);
handle.setXRot(Mth.clamp(pitch, -90, 90));
}
@ -98,10 +76,6 @@ public class RotationCommand extends AbstractCommand {
return (sender, args) -> {
var target = getTarget(sender, args);
this.turn(target, yaw, pitch);
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.turn.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE))
));
};
}
@ -112,10 +86,6 @@ public class RotationCommand extends AbstractCommand {
var target = getTarget(sender, args);
var rotation = Objects.requireNonNull((Rotation) args.get("rotation"));
this.turn(target, rotation.getYaw(), rotation.getPitch());
sender.sendMessage(miniMessage.deserialize(
"<gray>" + i18n.asString("fakeplayer.command.turn.success") + "</gray>",
Placeholder.component("name", text(target.getName(), WHITE))
));
}
/**

View File

@ -1,5 +1,6 @@
package io.github.hello09x.fakeplayer.core.command.impl;
import com.destroystokyo.paper.profile.PlayerProfile;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import dev.jorel.commandapi.executors.CommandArguments;
import io.github.hello09x.fakeplayer.core.Main;
@ -7,6 +8,7 @@ import org.apache.commons.lang3.mutable.MutableInt;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@ -33,31 +35,41 @@ public class SkinCommand extends AbstractCommand {
* 复制皮肤
*/
public void skin(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var player = Objects.requireNonNull((OfflinePlayer) args.get("player"));
var target = getTarget(sender, args);
var profile = target.getPlayerProfile();
var from = player.getPlayerProfile();
var copy = (Runnable) () -> {
profile.setTextures(from.getTextures());
from.getProperties().stream().filter(p -> p.getName().equals("textures")).findAny().ifPresent(profile::setProperty);
target.setPlayerProfile(profile);
};
if (!from.isComplete()) {
if (!sender.isOp() && cd.computeIfAbsent(sender, k -> new MutableInt()).getValue() != 0) {
sender.sendMessage(i18n.translate("fakeplayer.command.skin.error.too-many-operations", RED));
return;
}
Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> {
if (from.complete()) {
Bukkit.getScheduler().runTask(Main.getInstance(), copy);
}
cd.computeIfAbsent(sender, k -> new MutableInt()).setValue(COOL_DOWN_TICKS);
});
} else {
copy.run();
var player = Objects.requireNonNull((OfflinePlayer) args.get("player"));
var profile = player.getPlayerProfile();
if (profile.hasTextures()) {
this.setTextures(target, profile);
return;
}
// 拷贝离线玩家皮肤
if (!sender.isOp() && cd.computeIfAbsent(sender, k -> new MutableInt()).getValue() != 0) {
// 限制请求数, 防止 mojang api 限流
sender.sendMessage(i18n.translate("fakeplayer.command.skin.error.too-many-operations", RED));
return;
}
Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () -> {
// complete 会发起网络请求, 需要异步处理
if (profile.complete()) {
Bukkit.getScheduler().runTask(Main.getInstance(), () -> this.setTextures(target, profile));
}
cd.computeIfAbsent(sender, k -> new MutableInt()).setValue(COOL_DOWN_TICKS);
});
}
/**
* 设置皮肤
*
* @param target 目标玩家
* @param source 皮肤来源
*/
public void setTextures(@NotNull Player target, @NotNull PlayerProfile source) {
var profile = target.getPlayerProfile();
profile.setTextures(source.getTextures());
source.getProperties().stream().filter(p -> p.getName().equals("textures")).findAny().ifPresent(profile::setProperty);
target.setPlayerProfile(profile);
}
}

View File

@ -12,9 +12,9 @@ import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TpCommand extends AbstractCommand {
public class TeleportCommand extends AbstractCommand {
public final static TpCommand instance = new TpCommand();
public final static TeleportCommand instance = new TeleportCommand();
public void tp(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
@ -29,8 +29,8 @@ public class TpCommand extends AbstractCommand {
public void tps(@NotNull Player sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
var target = getTarget(sender, args);
var l1 = sender.getLocation().clone();
var l2 = target.getLocation().clone();
var l1 = sender.getLocation();
var l2 = target.getLocation();
Teleportor.teleportAndSound(target, l1);
Teleportor.teleportAndSound(sender, l2);

View File

@ -5,6 +5,7 @@ import io.github.hello09x.bedrock.i18n.I18n;
import io.github.hello09x.bedrock.task.Tasks;
import io.github.hello09x.fakeplayer.api.action.ActionSetting;
import io.github.hello09x.fakeplayer.api.action.ActionType;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.command.impl.RefillCommand;
@ -26,6 +27,7 @@ import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.net.InetAddress;
import java.time.LocalDateTime;
@ -34,7 +36,9 @@ import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
public class FakePlayer {
@ -77,6 +81,10 @@ public class FakePlayer {
@NotNull
private final UUID uuid;
@UnknownNullability
@Getter
private NMSGamePacketListener connection;
public FakePlayer(
@NotNull CommandSender creator,
@NotNull String creatorIp,
@ -89,7 +97,7 @@ public class FakePlayer {
this.creator = creator;
this.creatorIp = creatorIp;
this.sequenceName = sequenceName;
this.handle = Main.getVersionSupport().server(Bukkit.getServer()).newPlayer(uuid, name);
this.handle = Main.getBridge().server(Bukkit.getServer()).newPlayer(uuid, name);
this.player = handle.getPlayer();
this.ticker = new FakeplayerTicker(this, removeAt);
@ -97,6 +105,11 @@ public class FakePlayer {
player.setSleepingIgnored(true);
handle.setPlayBefore();
handle.unpersistAdvancements(Main.getInstance()); // 可避免一些插件的第一次入服欢迎信息
var displayName = text(player.getName(), GRAY, ITALIC);
player.playerListName(displayName);
player.displayName(displayName);
player.customName(displayName);
}
/**
@ -151,9 +164,9 @@ public class FakePlayer {
FakeplayerManager.instance.setRefillable(player, true);
}
var network = Main.getVersionSupport().network();
network.bindEmptyServerGamePacketListener(Bukkit.getServer(), this.player, address);
var network = Main.getBridge().network();
network.bindEmptyLoginPacketListener(Bukkit.getServer(), this.player, address);
this.connection = network.bindEmptyServerGamePacketListener(Bukkit.getServer(), this.player, address);
handle.configClientOptions(); // 处理皮肤设置问题
var spawnAt = option.spawnAt().clone();
@ -199,10 +212,6 @@ public class FakePlayer {
}
}
public @NotNull UUID getUUID() {
return this.uuid;
}
public boolean isOnline() {
return this.player.isOnline();
}
@ -248,4 +257,7 @@ public class FakePlayer {
return creator.getClass() == sender.getClass() && creator.getName().equals(sender.getName());
}
public @NotNull UUID getUUID() {
return uuid;
}
}

View File

@ -142,7 +142,7 @@ public class RefillListener implements Listener {
}
}
}, 1);
}, 1); // delay 1 是因为要等手上的物品在此 tick 消耗完
}
/**
@ -159,7 +159,7 @@ public class RefillListener implements Listener {
var replacement = inv.getItem(i);
if (replacement != null && replacement.isSimilar(item)) {
inv.setItem(slot, replacement);
inv.setItem(i, new ItemStack(Material.AIR));
inv.setItem(i, null);
return true;
}
}
@ -184,7 +184,7 @@ public class RefillListener implements Listener {
BlockFace.NORTH
);
if (!openEvent.callEvent()) {
// Could not open this inventory cause by other plugins
// 无法打开箱子
continue;
}
@ -196,7 +196,7 @@ public class RefillListener implements Listener {
var view = target.getOpenInventory();
var inv = view.getTopInventory();
if (inv.getType() != InventoryType.CHEST) {
// closed by other plugins
// 被其他插件取消了, 变成打开自己的背包了
return;
}
for (int i = inv.getSize() - 1; i >= 0; i--) {
@ -210,16 +210,17 @@ public class RefillListener implements Listener {
InventoryAction.MOVE_TO_OTHER_INVENTORY
);
if (!event.callEvent()) {
// canceled by other plugins
// 无法操作箱子
target.closeInventory(InventoryCloseEvent.Reason.PLAYER);
return;
}
target.getInventory().setItem(slot, replacement);
inv.setItem(i, new ItemStack(Material.AIR));
break;
inv.setItem(i, null);
target.closeInventory(InventoryCloseEvent.Reason.PLAYER);
return;
}
}
target.closeInventory(InventoryCloseEvent.Reason.PLAYER);
}, 20);
return;
}

View File

@ -7,6 +7,8 @@ import io.github.hello09x.bedrock.util.Components;
import io.github.hello09x.fakeplayer.api.action.ActionSetting;
import io.github.hello09x.fakeplayer.api.action.ActionType;
import io.github.hello09x.fakeplayer.api.constant.ConstantPool;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener.ReceivedMessage;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.entity.FakePlayer;
@ -35,10 +37,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@ -74,7 +73,7 @@ public class FakeplayerManager {
private FakeplayerManager() {
var timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(() -> {
timer.scheduleWithFixedDelay(() -> {
if (Bukkit.getServer().getTPS()[1] < config.getKaleTps()) {
Tasks.run(() -> {
if (FakeplayerManager.this.removeAll("low tps") > 0) {
@ -116,9 +115,7 @@ public class FakeplayerManager {
sn,
removeAt
);
var target = fp.getPlayer();
target.playerListName(text(target.getName(), GRAY, ITALIC));
return CompletableFuture
.supplyAsync(() -> {
@ -419,6 +416,24 @@ public class FakeplayerManager {
return target.hasMetadata("fakeplayer:refillable");
}
public @Nullable ReceivedMessage getLastMessage(@NotNull Player target) {
return Optional.ofNullable(playerList.getByUUID(target.getUniqueId()))
.map(FakePlayer::getConnection)
.map(NMSGamePacketListener::getLastMessage)
.orElse(null);
}
public @NotNull List<ReceivedMessage> getMessages(@NotNull Player target, int skip, int size) {
return Optional.ofNullable(playerList.getByUUID(target.getUniqueId()))
.map(FakePlayer::getConnection)
.map(NMSGamePacketListener::getRecentMessages)
.orElse(Collections.emptyList())
.stream()
.skip(skip)
.limit(size)
.toList();
}
/**
* 以假人身份执行命令
*
@ -471,19 +486,19 @@ public class FakeplayerManager {
}
}
public boolean openInventory(@NotNull Player creator, @NotNull Player player) {
var target = this.playerList.getByName(player.getName());
if (target == null) {
public boolean openInventory(@NotNull Player creator, @NotNull Player target) {
var fp = this.playerList.getByName(target.getName());
if (fp == null) {
return false;
}
if (!creator.isOp() && !target.isCreator(creator)) {
if (!creator.isOp() && !fp.isCreator(creator)) {
return false;
}
if (!openInvDepend.openInventory(creator, player)) {
this.openInventoryDefault(creator, player);
if (!openInvDepend.openInventory(creator, target)) {
this.openInventoryDefault(creator, target);
}
creator.playSound(player.getLocation(), Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.5f, 1.0f);
creator.playSound(target.getLocation(), Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.5f, 1.0f);
return true;
}

View File

@ -37,7 +37,7 @@ public class ActionManager {
@NotNull ActionSetting setting
) {
var managers = this.managers.computeIfAbsent(player.getUniqueId(), key -> new HashMap<>());
managers.put(action, Main.getVersionSupport().createAction(player, action, setting));
managers.put(action, Main.getBridge().createAction(player, action, setting));
}
public void tick() {

View File

@ -13,11 +13,11 @@ import java.util.Optional;
import java.util.function.Function;
/**
* @param name 配置项 key
* @param name 配置项 key
* @param translationKey 翻译 key
* @param defaultValue 默认值
* @param options 可选值
* @param converter 转换器
* @param defaultValue 默认值
* @param options 可选值
* @param converter 转换器
*/
public record Config<T>(
@ -128,14 +128,14 @@ public record Config<T>(
private static <T> Config<T> build(
@NotNull String name,
@NotNull String translateKey,
@NotNull String translationKey,
@NotNull Class<T> type,
@NotNull T defaultValue,
@NotNull List<String> options,
@Nullable String permission,
@NotNull Function<String, T> converter
) {
var config = new Config<>(name, translateKey, type, defaultValue, options, permission, converter);
var config = new Config<>(name, translationKey, type, defaultValue, options, permission, converter);
values.put(name, config);
return config;
}

View File

@ -56,7 +56,6 @@ fakeplayer.command.look.east.description=Look east
fakeplayer.command.look.entity.description=Look at nearest entity
fakeplayer.command.look.north.description=Look north
fakeplayer.command.look.south.description=Look south
fakeplayer.command.look.success=<name> is looking <direction> now
fakeplayer.command.look.up.description=Look up
fakeplayer.command.look.west.description=Look west
fakeplayer.command.mine.description=Mine
@ -65,7 +64,6 @@ fakeplayer.command.move.description=Move
fakeplayer.command.move.forward.description=Move forward
fakeplayer.command.move.left.description=Move left
fakeplayer.command.move.right.description=Move right
fakeplayer.command.move.success=<name> moved
fakeplayer.command.refill.description=Config whether auto replacing their broken tools, or items
fakeplayer.command.refill.success=<name> refillable was set to <status>
fakeplayer.command.reload.description=Reload config file

View File

@ -56,7 +56,6 @@ fakeplayer.command.look.east.description=\u5411\u4E1C\u770B
fakeplayer.command.look.entity.description=\u770B\u5411\u9644\u8FD1\u5B9E\u4F53
fakeplayer.command.look.north.description=\u5411\u5317\u770B
fakeplayer.command.look.south.description=\u5411\u5357\u770B
fakeplayer.command.look.success=<name> \u770B\u5411 <direction>
fakeplayer.command.look.up.description=\u5411\u4E0A\u770B
fakeplayer.command.look.west.description=\u5411\u897F\u770B
fakeplayer.command.mine.description=\u6316\u6398
@ -65,7 +64,6 @@ fakeplayer.command.move.description=\u79FB\u52A8
fakeplayer.command.move.forward.description=\u5411\u524D\u79FB\u52A8
fakeplayer.command.move.left.description=\u5411\u5DE6\u79FB\u52A8
fakeplayer.command.move.right.description=\u5411\u53F3\u79FB\u52A8
fakeplayer.command.move.success=<name> \u52A8\u4E86\u4E00\u4E0B
fakeplayer.command.refill.description=\u8BBE\u7F6E\u81EA\u52A8\u586B\u88C5
fakeplayer.command.refill.success=<name> \u81EA\u52A8\u88C5\u586B\u5DF2 <status>
fakeplayer.command.reload.description=\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6

View File

@ -19,6 +19,16 @@
<dependencies>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-api</artifactId>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-v1_20_R1</artifactId>

View File

@ -0,0 +1,2 @@
io.github.hello09x.fakeplayer.v1_20_R1.spi.NMSBridgeImpl
io.github.hello09x.fakeplayer.v1_20_R2.spi.NMSBridgeImpl

View File

@ -1,2 +0,0 @@
io.github.hello09x.fakeplayer.v1_20_R1.spi.VersionSupportImpl
io.github.hello09x.fakeplayer.v1_20_R2.spi.VersionSupportImpl

View File

@ -32,6 +32,7 @@
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -1,18 +1,27 @@
package io.github.hello09x.fakeplayer.v1_20_R1.network;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.api.utils.ClientboundSystemChatPackets;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.protocol.game.ClientboundSystemChatPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.entity.player.Player;
import org.apache.commons.lang3.mutable.MutableInt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class EmptyServerGamePacketListener extends ServerGamePacketListenerImpl {
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static net.kyori.adventure.text.Component.text;
public class EmptyServerGamePacketListener extends ServerGamePacketListenerImpl implements NMSGamePacketListener {
private final LinkedList<ReceivedMessage> messages = new LinkedList<>();
private final MutableInt messageId = new MutableInt();
public EmptyServerGamePacketListener(@NotNull MinecraftServer minecraftServer, @NotNull Connection networkManager, @NotNull ServerPlayer player) {
super(minecraftServer, networkManager, player);
@ -20,6 +29,34 @@ public class EmptyServerGamePacketListener extends ServerGamePacketListenerImpl
@Override
public void send(Packet<?> packet) {
if (packet instanceof ClientboundSystemChatPacket chat) {
this.handleSystemChatPacket(chat);
}
}
private void handleSystemChatPacket(@NotNull ClientboundSystemChatPacket packet) {
if (this.messages.size() >= MESSAGE_HISTORY_SIZE) {
this.messages.removeFirst();
}
var content = ClientboundSystemChatPackets.getAdventureContent(packet);
if (content == null) {
content = text(packet.content());
}
this.messages.addLast(new ReceivedMessage(messageId.incrementAndGet(), content));
}
@Override
public @Nullable ReceivedMessage getLastMessage() {
if (this.messages.isEmpty()) {
return null;
}
return this.messages.getLast();
}
@Override
public @NotNull List<ReceivedMessage> getRecentMessages() {
return new ArrayList<>(this.messages);
}
}

View File

@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Set;
public class VersionSupportImpl implements VersionSupport {
public class NMSBridgeImpl implements NMSBridge {
private final static Set<String> SUPPORTS = Set.of("1.20", "1.20.1");

View File

@ -1,5 +1,6 @@
package io.github.hello09x.fakeplayer.v1_20_R1.spi;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
import io.github.hello09x.fakeplayer.v1_20_R1.network.EmptyConnection;
import io.github.hello09x.fakeplayer.v1_20_R1.network.EmptyLoginPacketListener;
@ -16,7 +17,7 @@ import java.net.InetAddress;
public class NMSNetworkImpl implements NMSNetwork {
@Override
public void bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address) {
public @NotNull NMSGamePacketListener bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address) {
var connect = new EmptyConnection(PacketFlow.SERVERBOUND, address);
var listener = new EmptyServerGamePacketListener(
((CraftServer) server).getServer(),
@ -24,6 +25,7 @@ public class NMSNetworkImpl implements NMSNetwork {
((CraftPlayer) player).getHandle()
);
connect.setListener(listener);
return listener;
}
@Override

View File

@ -75,7 +75,6 @@ public class NMSServerPlayerImpl implements NMSServerPlayer {
@Override
public void doTick() {
handle.doTick();
;
}
@Override

View File

@ -27,11 +27,13 @@
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -1,19 +1,64 @@
package io.github.hello09x.fakeplayer.v1_20_R2.network;
import io.github.hello09x.fakeplayer.api.spi.NMSGamePacketListener;
import io.github.hello09x.fakeplayer.api.utils.ClientboundSystemChatPackets;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSystemChatPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.apache.commons.lang3.mutable.MutableInt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static net.kyori.adventure.text.Component.text;
public class EmptyServerGamePacketListener extends ServerGamePacketListenerImpl implements NMSGamePacketListener {
private final LinkedList<ReceivedMessage> messages = new LinkedList<>();
private final MutableInt messageId = new MutableInt();
public class EmptyServerGamePacketListener extends ServerGamePacketListenerImpl {
public EmptyServerGamePacketListener(MinecraftServer minecraftServer, Connection networkManager, ServerPlayer entityPlayer, CommonListenerCookie commonListenerCookie) {
super(minecraftServer, networkManager, entityPlayer, commonListenerCookie);
}
@Override
public void send(Packet<?> packet) {
if (packet instanceof ClientboundSystemChatPacket chat) {
this.handleSystemChatPacket(chat);
}
}
private void handleSystemChatPacket(@NotNull ClientboundSystemChatPacket packet) {
if (this.messages.size() >= MESSAGE_HISTORY_SIZE) {
this.messages.removeFirst();
}
var content = ClientboundSystemChatPackets.getAdventureContent(packet);
if (content == null) {
content = text(packet.content());
}
this.messages.addLast(new ReceivedMessage(messageId.incrementAndGet(), content));
}
@Override
public @Nullable ReceivedMessage getLastMessage() {
if (this.messages.isEmpty()) {
return null;
}
return this.messages.getLast();
}
@Override
public @NotNull List<ReceivedMessage> getRecentMessages() {
return new ArrayList<>(this.messages);
}
}

View File

@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Set;
public class VersionSupportImpl implements VersionSupport {
public class NMSBridgeImpl implements NMSBridge {
private final static Set<String> SUPPORTS = Set.of("1.20.2");

View File

@ -17,7 +17,7 @@ import java.net.InetAddress;
public class NMSNetworkImpl implements NMSNetwork {
@Override
public void bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address) {
public @NotNull EmptyServerGamePacketListener bindEmptyServerGamePacketListener(@NotNull Server server, @NotNull Player player, @NotNull InetAddress address) {
var connect = new EmptyConnection(PacketFlow.CLIENTBOUND, address);
var listener = new EmptyServerGamePacketListener(
((CraftServer) server).getServer(),
@ -26,6 +26,7 @@ public class NMSNetworkImpl implements NMSNetwork {
CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile())
);
connect.setListener(listener);
return listener;
}
@Override