Lifecycle commands

This commit is contained in:
tanyaofei 2024-08-07 10:47:43 +08:00
parent a4961e09b3
commit e050f8772a
18 changed files with 285 additions and 192 deletions

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-api</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -1,76 +0,0 @@
package io.github.hello09x.fakeplayer.api.event;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
public class FakePlayerSpawnEvent extends PlayerEvent implements Cancellable {
private final static HandlerList HANDLERS = new HandlerList();
@NotNull
@Getter
private final CommandSender creator;
@NotNull
@Getter
private Result result = Result.ALLOW;
@NotNull
@Getter
private Component reason = Component.empty();
public FakePlayerSpawnEvent(@NotNull CommandSender creator, @NotNull Player who) {
super(who);
this.creator = creator;
}
public static @NotNull HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
@Override
public boolean isCancelled() {
return this.result != Result.ALLOW;
}
/**
* @param cancel true if you wish to cancel this event
* @deprecated 请使用 {@link #disallow(Component)} (Result, Component)}
*/
@Override
@Deprecated
public void setCancelled(boolean cancel) {
this.disallow(Component.empty());
}
public void allow() {
this.result = Result.ALLOW;
this.reason = Component.empty();
}
public void disallow(@NotNull Component reason) {
this.result = Result.DISALLOW;
this.reason = reason;
}
public enum Result {
ALLOW,
DISALLOW
}
}

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-core</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -9,6 +9,7 @@ import io.github.hello09x.devtools.core.utils.Exceptions;
import io.github.hello09x.devtools.database.DatabaseModule;
import io.github.hello09x.fakeplayer.core.command.CommandRegistry;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.listener.FakeplayerLifecycleListener;
import io.github.hello09x.fakeplayer.core.listener.FakeplayerListener;
import io.github.hello09x.fakeplayer.core.listener.PlayerListener;
import io.github.hello09x.fakeplayer.core.listener.ReplenishListener;
@ -58,6 +59,7 @@ public final class Main extends JavaPlugin {
{
var manager = getServer().getPluginManager();
manager.registerEvents(injector.getInstance(PlayerListener.class), this);
manager.registerEvents(injector.getInstance(FakeplayerLifecycleListener.class), this);
manager.registerEvents(injector.getInstance(FakeplayerListener.class), this);
manager.registerEvents(injector.getInstance(ReplenishListener.class), this);
}

View File

@ -3,7 +3,7 @@ package io.github.hello09x.fakeplayer.core.command;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import dev.jorel.commandapi.CommandPermission;
import io.github.hello09x.devtools.command.exception.HelpCommand;
import io.github.hello09x.devtools.command.HelpCommand;
import io.github.hello09x.devtools.core.utils.ComponentUtils;
import io.github.hello09x.fakeplayer.api.spi.Action;
import io.github.hello09x.fakeplayer.core.command.impl.*;
@ -15,7 +15,7 @@ import org.bukkit.entity.LivingEntity;
import java.util.List;
import static io.github.hello09x.devtools.command.exception.Commands.*;
import static io.github.hello09x.devtools.command.Commands.*;
import static io.github.hello09x.fakeplayer.core.command.CommandSupports.*;
import static net.kyori.adventure.text.Component.translatable;

View File

@ -21,8 +21,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static io.github.hello09x.devtools.command.exception.Commands.command;
import static io.github.hello09x.devtools.command.exception.Commands.int32;
import static io.github.hello09x.devtools.command.Commands.command;
import static io.github.hello09x.devtools.command.Commands.int32;
import static net.kyori.adventure.text.Component.translatable;
public abstract class CommandSupports {

View File

@ -111,7 +111,8 @@ public class FakePlayerCommandArgument extends Argument<CommandResult> implement
// Get location sender is looking at if they are a Player, matching vanilla behavior
// No builtin Commands use the location parameter, but they could
Location location = null;
if (sender instanceof Player player) {
if (sender instanceof Player) {
var player = (Player) sender;
Block block = player.getTargetBlockExact(5, FluidCollisionMode.NEVER);
if (block != null) {
location = block.getLocation();

View File

@ -2,7 +2,7 @@ package io.github.hello09x.fakeplayer.core.command.impl;
import com.google.inject.Singleton;
import dev.jorel.commandapi.executors.CommandArguments;
import io.github.hello09x.devtools.command.exception.Page;
import io.github.hello09x.devtools.command.Page;
import io.github.hello09x.fakeplayer.core.command.Permission;
import io.github.hello09x.fakeplayer.core.util.Mth;
import org.apache.commons.lang3.StringUtils;

View File

@ -62,20 +62,35 @@ public class FakeplayerConfig extends PluginConfig {
private int kaleTps;
/**
* 准备命令
* 创建前执行命令
*/
private List<String> preparingCommands;
private List<String> preSpawnCommands;
/**
* 创建时执行命令
*/
private List<String> postSpawnCommands;
/**
* 创建后执行命令
*/
private List<String> afterSpawnCommands;
/**
* 退出前执行命令
*/
private List<String> postQuitCommands;
/**
* 退出后命令
*/
private List<String> afterQuitCommands;
/**
* 自执行命令
*/
private List<String> selfCommands;
/**
* 销毁命令
*/
private List<String> destroyCommands;
/**
* 退出时是否丢弃背包物品
*/
@ -140,8 +155,14 @@ public class FakeplayerConfig extends PluginConfig {
this.detectIp = file.getBoolean("detect-ip", false);
this.kaleTps = file.getInt("kale-tps", 0);
this.selfCommands = file.getStringList("self-commands");
this.preparingCommands = file.getStringList("preparing-commands");
this.destroyCommands = file.getStringList("destroy-commands");
this.preSpawnCommands = file.getStringList("pre-spawn-commands");
this.postSpawnCommands = file.getStringList("post-spawn-commands");
deprecated:
this.afterSpawnCommands = file.getStringList("after-spawn-commands");
this.postQuitCommands = file.getStringList("post-quit-commands");
this.afterQuitCommands = file.getStringList("after-quit-commands");
// this.preparingCommands = file.getStringList("preparing-commands");
// this.destroyCommands = file.getStringList("destroy-commands");
this.nameTemplate = file.getString("name-template", "");
this.dropInventoryOnQuiting = file.getBoolean("drop-inventory-on-quiting", true);
this.persistData = file.getBoolean("persist-data", true);
@ -169,6 +190,19 @@ public class FakeplayerConfig extends PluginConfig {
if (!this.allowCommands.isEmpty()) {
log.warning("allow-commands is deprecated which will be removed at 0.4.0, you should use Permissions Plugin to assign permission groups to fake players.");
}
var preparingCommands = file.getStringList("preparing-commands");
if (!preparingCommands.isEmpty()) {
log.warning("preparing-commands is deprecated, use post-spawn-commands instead.");
this.postSpawnCommands.addAll(preparingCommands);
}
var destroyCommands = file.getStringList("destroy-commands");
if (!destroyCommands.isEmpty()) {
log.warning("destroy-commands is deprecated, use post-quit-commands instead.");
this.postQuitCommands.addAll(destroyCommands);
}
}
private @Nullable Duration getLifespan(@NotNull FileConfiguration file) {

View File

@ -4,7 +4,6 @@ import io.github.hello09x.devtools.core.message.RuntimeMessageException;
import io.github.hello09x.devtools.core.utils.EntityUtils;
import io.github.hello09x.devtools.core.utils.SchedulerUtils;
import io.github.hello09x.devtools.core.utils.WorldUtils;
import io.github.hello09x.fakeplayer.api.event.FakePlayerSpawnEvent;
import io.github.hello09x.fakeplayer.api.spi.Action;
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
@ -141,17 +140,6 @@ public class FakePlayer {
}
}
{
var event = this.callSpawnEvent();
if (event.isCancelled() && config.getPreventKicking().ordinal() < PreventKicking.ON_SPAWNING.ordinal()) {
throw new RuntimeMessageException(translatable(
"fakeplayer.command.spawn.error.disallowed", RED,
text(player.getName(), WHITE),
event.getReason()
));
}
}
if (config.isDropInventoryOnQuiting()) {
// 跨服背包同步插件可能导致假人既丢弃了一份到地上在重新生成的时候又回来了
// 因此在生成的时候清空一次背包
@ -242,12 +230,6 @@ public class FakePlayer {
return event;
}
private @NotNull FakePlayerSpawnEvent callSpawnEvent() {
var event = new FakePlayerSpawnEvent(this.creator, this.player);
Bukkit.getPluginManager().callEvent(event);
return event;
}
/**
* 判断是否是创建者
* <p>如果玩家下线再重新登陆, entityID 将会不一样导致 {@link Object#equals(Object)} 返回 {@code false}</p>

View File

@ -0,0 +1,85 @@
package io.github.hello09x.fakeplayer.core.listener;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.config.FakeplayerConfig;
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull;
/**
* @author tanyaofei
* @since 2024/8/7
**/
@Singleton
public class FakeplayerLifecycleListener implements Listener {
private final FakeplayerManager manager;
private final FakeplayerConfig config;
@Inject
public FakeplayerLifecycleListener(FakeplayerManager manager, FakeplayerConfig config) {
this.manager = manager;
this.config = config;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPostSpawn(@NotNull PlayerJoinEvent event) {
var player = event.getPlayer();
if (this.manager.isNotFake(player)) {
// Not a fake player
return;
}
manager.dispatchCommands(player, config.getPostSpawnCommands());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onAfterSpawn(@NotNull PlayerJoinEvent event) {
var player = event.getPlayer();
if (this.manager.isNotFake(player)) {
// Not a fake player
return;
}
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
if (player.isOnline()) {
manager.dispatchCommands(player, config.getAfterSpawnCommands());
manager.issueCommands(player, config.getSelfCommands());
}
}, 20);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPostQuit(@NotNull PlayerQuitEvent event) {
var player = event.getPlayer();
if (this.manager.isNotFake(player)) {
// Not a fake player
return;
}
manager.dispatchCommands(player, config.getPostQuitCommands());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onAfterQuit(@NotNull PlayerQuitEvent event) {
var player = event.getPlayer();
if (this.manager.isNotFake(player)) {
// Not a fake player
return;
}
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
manager.dispatchCommands(player, config.getAfterQuitCommands());
}, 20);
}
}

View File

@ -1,6 +1,5 @@
package io.github.hello09x.fakeplayer.core.listener;
import com.google.common.base.Throwables;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.github.hello09x.devtools.core.utils.ComponentUtils;
@ -60,7 +59,7 @@ public class FakeplayerListener implements Listener {
* 拒绝真实玩家使用假人用过的 ID 登陆
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onLogin(@NotNull PlayerLoginEvent event) {
public void rejectForUsedUUID(@NotNull PlayerLoginEvent event) {
var player = event.getPlayer();
if (InternalAddressGenerator.canBeGenerated(event.getAddress())) {
@ -119,7 +118,7 @@ public class FakeplayerListener implements Listener {
* 死亡退出游戏
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onDead(@NotNull PlayerDeathEvent event) {
public void kickOnDead(@NotNull PlayerDeathEvent event) {
var player = event.getPlayer();
if (manager.isNotFake(player)) {
return;
@ -140,29 +139,24 @@ public class FakeplayerListener implements Listener {
/**
* 退出游戏掉落背包
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onQuit(@NotNull PlayerQuitEvent event) {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void cleanup(@NotNull PlayerQuitEvent event) {
var target = event.getPlayer();
if (manager.isNotFake(target)) {
return;
}
try {
manager.dispatchCommands(target, config.getDestroyCommands());
// 如果移除玩家后没有假人, 则更新命令列表
// 这个方法需要在 cleanup 之前执行, 不然无法获取假人的创建者
if (manager.getCreator(target) instanceof Player creator && manager.countByCreator(creator) == 1) {
Bukkit.getScheduler().runTaskLater(Main.getInstance(), creator::updateCommands, 1); // 需要下 1 tick 移除后才正确刷新
}
} catch (Throwable e) {
log.warning("执行 destroy-commands 时发生错误: \n" + Throwables.getStackTraceAsString(e));
} finally {
manager.cleanup(target);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void autoFish(@NotNull PlayerFishEvent event) {
public void autoFishing(@NotNull PlayerFishEvent event) {
if (event.getState() != PlayerFishEvent.State.BITE) {
return;
}

View File

@ -109,8 +109,10 @@ public class FakeplayerManager {
sn,
lifespan
);
var target = fp.getPlayer(); // 即使出现异常也不需要处理这个玩家, 最终会被 GC
this.dispatchCommandsEarly(fp, this.config.getPreSpawnCommands());
return CompletableFuture
.supplyAsync(() -> {
var configs = configManager.getConfigs(creator);
@ -126,15 +128,8 @@ public class FakeplayerManager {
);
})
.thenComposeAsync(fp::spawnAsync)
.thenApply(nul -> {
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
this.playerList.add(fp);
});
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
this.dispatchCommands(target, config.getPreparingCommands());
this.issueCommands(target, config.getSelfCommands());
}, 20);
.thenApply(ignored -> {
Bukkit.getScheduler().runTask(Main.getInstance(), () -> playerList.add(fp));
return target;
});
}
@ -451,7 +446,10 @@ public class FakeplayerManager {
return;
}
for (var cmd : Commands.formatCommands(commands)) {
var p = target.getName();
var u = target.getUniqueId().toString();
var c = Objects.requireNonNull(this.getCreatorName(target));
for (var cmd : Commands.formatCommands(commands, "%p", p, "%u", u, "%c", c)) {
if (!target.performCommand(cmd)) {
log.warning(target.getName() + " failed to execute command: " + cmd);
} else {
@ -460,6 +458,25 @@ public class FakeplayerManager {
}
}
public void dispatchCommandsEarly(@NotNull FakePlayer fp, @NotNull List<String> commands) {
if (commands.isEmpty()) {
return;
}
var server = Bukkit.getServer();
var sender = Bukkit.getConsoleSender();
var p = fp.getName();
var u = fp.getUUID().toString();
var c = fp.getCreator().getName();
for (var cmd : Commands.formatCommands(commands, "%p", p, "%u", u, "%c", c)) {
if (!server.dispatchCommand(sender, cmd)) {
log.warning("Failed to execute command for %s: ".formatted(p) + cmd);
} else {
log.info("Dispatched command: " + cmd);
}
}
}
/**
* 以控制台身份对玩家执行命令
*
@ -471,20 +488,15 @@ public class FakeplayerManager {
return;
}
if (this.isNotFake(player)) {
return;
}
var server = Bukkit.getServer();
var sender = Bukkit.getConsoleSender();
for (var cmd : Commands.formatCommands(
commands,
"%p", player.getName(),
"%u", player.getUniqueId().toString(),
"%c", Objects.requireNonNull(this.getCreatorName(player)))
) {
var p = player.getName();
var u = player.getUniqueId().toString();
var c = Objects.requireNonNull(this.getCreatorName(player));
for (var cmd : Commands.formatCommands(commands, "%p", p, "%u", u, "%c", c)) {
if (!server.dispatchCommand(sender, cmd)) {
log.warning("Failed to execute command for %s: ".formatted(player.getName()) + cmd);
log.warning("Failed to execute command for %s: ".formatted(p) + cmd);
} else {
log.info("Dispatched command: " + cmd);
}
@ -494,20 +506,20 @@ public class FakeplayerManager {
/**
* 让玩家打开假人背包
*
* @param creator 玩家
* @param viewer 玩家
* @param target 假人
* @return 是否打开成功
*/
public boolean openInventory(@NotNull Player creator, @NotNull Player target) {
public boolean openInventory(@NotNull Player viewer, @NotNull Player target) {
var fp = this.playerList.getByName(target.getName());
if (fp == null) {
return false;
}
if (!creator.isOp() && !fp.isCreator(creator)) {
if (!viewer.isOp() && !fp.isCreator(viewer)) {
return false;
}
this.invsee.openInventory(creator, target);
this.invsee.openInventory(viewer, target);
var pos = target.getLocation();
pos.getWorld().playSound(pos, Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.3f, 1.0f);
return true;

View File

@ -111,51 +111,110 @@ kick-on-dead: true
# It's not recommended to enable this option, as it may cause the redstone machine to malfunction
kale-tps: 0
# 预准备命令
# 假人诞生时会以控制台的身份按顺序执行以下命令, 这些命令会比 `self-commands` 更早执行
# 你可以用这个来实现权限组的分配之类的命令
# 占位符:
# %p: 假人名称
# %u: 假人 uuid
# %c: 创建者的名称
# Server will execute the following commands after the fake player was spawned
# You can add some commands to give them permission, such as '/lp user %p permission set xxx true'
# placeholder:
# Pre-Spawn-Commands
# Server will execute the following commands BEFORE trying to spawn a fake player.
# This is helpful for adding fake player into whitelist
# Variables:
# %p: the name of the fake player
# %u: the uuid of the fake player
# %c: the name of creator
preparing-commands:
# %u: the UUID of the fake player
# %c: the name of the creator
# 服务器会在假人创建前执行这些命令
# 这里可以添加类似于白名单的命令来保证后续创建过程的正常执行
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
pre-spawn-commands:
- ''
- ''
# 假人销毁时执行的命令
# 与 `preparing-commands` 类似, 会在假人销毁时依次执行的命令
# 也许可以用来销毁第三方插件的档案?
# 占位符:
# %p: 假人名称
# %u: 假人 uuid
# %c: 创建者的名称
# Server will execute the following commands before the fake player was quited(PlayerQuitEvent)
# you can add some commands to clean up data
# placeholder:
# Post-Spawn-Commands
# Server will execute the following commands DURING spawning (in PlayerJoinEvent).
# Variables:
# %p: the name of the fake player
# %u: the uuid of the fake player
# %c: the name of creator
destroy-commands:
# %u: the UUID of the fake player
# %c: the name of the creator
# 服务器会在假人正在加入游戏中执行这些命令
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
post-spawn-commands:
- ''
- ''
# 自执行命令
# 假人在诞生时会以自己的身份按顺序执行命令
# 你可以在这里做添加 /register 和 /login 命令来防止 `AuthMe` 等插件踢掉超时未登陆的玩家
# The fake player will execute the following commands
# You can add some command to make him to login
# - '/register ANY_PASSWORD'
# - '/login ANY_PASSWORD'
# After-Spawn-Commands
# Server will execute the following commands AFTER the fake player was spawned (after PlayerJoinEvent).
# This is helpful for giving permission.
# Variables:
# %p: the name of the fake player
# %u: the UUID of the fake player
# %c: the name of the creator
# 服务器会在假人创建后执行这里的命令
# 这里可以添加一些权限组分配之类的命令
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
after-spawn-commands:
- ''
- ''
# Self-Commands
# The fake player will execute the following commands AFTER they were joined the server.
# This is helpful for them to execute some login command.
# Ensure the password is complex enough otherwise the login plugin might reject it.
# Variables:
# %p: the name of the fake player
# %u: the UUID of the fake player
# %c: the name of the creator
# 假人在加入游戏后会执行以下命令
# 你可以添加一些登陆命令来让他们完成登陆过程
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
self-commands:
- ''
- ''
# Post-Destroy-Commands
# Server will execute the following commands when the fake player is quiting but not quited (in PlayerQuitEvent)
# This is helpful for cleaning up their inventory if you want it
# Variables:
# %p: the name of the fake player
# %u: the UUID of the fake player
# %c: the name of the creator
# 服务器会在假人正在退出时执行这些命令, 命令执行的那一刻假人还位于服务器
# 你可以添加一些用来清空假人背包之类的命令
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
post-quit-commands:
- ''
- ''
# Post-Destroy-Commands
# Server will execute the following commands AFTER the fake player was quited. (after PlayerQuitEvent)
# Variables:
# %p: the name of the fake player
# %u: the UUID of the fake player
# %c: the name of the creator
# 销毁后命令
# 服务器会在假人退出游戏之后执行这些命令
# 你可以添加一些清理白名单、取消权限分配、清理某些插件数据等命令
# 变量:
# %p: 假人的名称
# %u: 假人的 UUID
# %c: 创建人的名称
after-quit-commands:
- ''
- ''
# 允许玩家让假人执行的命令
# 在这里你可以放一些你服务器的命令,玩家就可以执行
# 例如添加 /sit 之后, 玩家可以通过 '/fp cmd myfakeplayer sit' 让假人坐下来

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-v1_20_R1</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-v1_20_R2</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-v1_20_R3_R4</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -12,8 +12,8 @@
<artifactId>fakeplayer-v1_20_R5_R6</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>