Compare commits

...

7 Commits

Author SHA1 Message Date
tanyaofei
2e6a3a34d9 update README 2025-08-15 09:40:41 +08:00
tanyaofei
07340d3218 optimize code 2025-08-15 09:29:28 +08:00
tanyaofei
6a3d880f17 config: bump version to 19 2025-08-15 09:26:51 +08:00
tanyaofei
f261e16a23 config: 'name-prefix' and 'name-style' 2025-08-15 09:26:41 +08:00
tanyaofei
d4bc411536 bugfix: error spawning fake player 2025-08-15 09:26:14 +08:00
tanyaofei
07eaa9774a Bump version to 0.3.18 2025-07-25 11:36:10 +08:00
tanyaofei
62b6da6c81 Add supports for 1.21.8 2025-07-25 11:34:34 +08:00
11 changed files with 265 additions and 17 deletions

View File

@ -47,7 +47,7 @@ This approach can let you preview new content when you are upgrading it.
| /fp invsee | Open an inventory of a fake player | fakeplayer.command.invsee | Right-clicking on fake players has the same effect | | /fp invsee | Open an inventory of a fake player | fakeplayer.command.invsee | Right-clicking on fake players has the same effect |
| /fp sleep | Sleep | fakeplayer.command.sleep | | | /fp sleep | Sleep | fakeplayer.command.sleep | |
| /fp wakeup | Wake up | fakeplayer.command.wakeup | | | /fp wakeup | Wake up | fakeplayer.command.wakeup | |
| /fp status | Show player status | fakeplayer.command.status | | | /fp status | Show status | fakeplayer.command.status | |
| /fp respawn | Respawn a dead fake player | fakeplayer.command.respawn | Available when server config does not kick on fake player death | | /fp respawn | Respawn a dead fake player | fakeplayer.command.respawn | Available when server config does not kick on fake player death |
| /fp tp | Teleport to a fake player | fakeplayer.command.tp | | | /fp tp | Teleport to a fake player | fakeplayer.command.tp | |
| /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | | | /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | |
@ -167,13 +167,13 @@ If your server does not restrict various player commands, you can use this direc
4. Edit your `config.yml`, set `i18n.locale` to the name suffix which you just created such as `en_us` 4. Edit your `config.yml`, set `i18n.locale` to the name suffix which you just created such as `en_us`
5. Type `/fp reload-translation` to reload translation file. If you change `i18n.local`, you should `/fp reload` first 5. Type `/fp reload-translation` to reload translation file. If you change `i18n.local`, you should `/fp reload` first
**Make sure the translation file is encoding with UTF-8** **Make sure the translation file is encoded with UTF-8**
# FAQs # FAQs
## xxx lost connection: PacketEvents 2.0 failed to inject ## xxx lost connection: PacketEvents 2.0 failed to inject
Some plugin change the `Connection` of the fake player, You can set `prevent-kicking` to `ALWAYS` to solve it. Some plugin changes the `Connection` of the fake player, You can set `prevent-kicking` to `ALWAYS` to solve it.
```yaml ```yaml
# config.yml # config.yml

View File

@ -10,16 +10,15 @@ import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.core.repository.model.Feature; import io.github.hello09x.fakeplayer.core.repository.model.Feature;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.time.Duration; import java.time.Duration;
import java.util.Arrays; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -52,6 +51,21 @@ public class FakeplayerConfig extends PluginConfig {
*/ */
private String nameTemplate; private String nameTemplate;
/**
* 假人名称前缀
*/
private String namePrefix;
/**
* 名称样式, 颜色
*/
private NamedTextColor nameStyleColor;
/**
* 名称样式, 格式
*/
private List<TextDecoration> nameStyleDecorations;
/** /**
* 创建者玩家下线时是否跟随下线 * 创建者玩家下线时是否跟随下线
*/ */
@ -187,6 +201,7 @@ public class FakeplayerConfig extends PluginConfig {
this.namePattern = getNamePattern(file); this.namePattern = getNamePattern(file);
this.preventKicking = this.getPreventKicking(file); this.preventKicking = this.getPreventKicking(file);
this.nameTemplate = getNameTemplate(file); this.nameTemplate = getNameTemplate(file);
this.namePrefix = file.getString("name-prefix", "");
this.lifespan = getLifespan(file); this.lifespan = getLifespan(file);
this.allowCommands = file.getStringList("allow-commands") this.allowCommands = file.getStringList("allow-commands")
.stream() .stream()
@ -199,6 +214,8 @@ public class FakeplayerConfig extends PluginConfig {
.collect(Collectors.toMap(Function.identity(), key -> file.getString("default-features." + key.name(), key.getDefaultOption()))); .collect(Collectors.toMap(Function.identity(), key -> file.getString("default-features." + key.name(), key.getDefaultOption())));
this.invseeImplement = ConfigUtils.getEnum(file, "invsee-implement", InvseeImplement.class, InvseeImplement.AUTO); this.invseeImplement = ConfigUtils.getEnum(file, "invsee-implement", InvseeImplement.class, InvseeImplement.AUTO);
this.debug = file.getBoolean("debug", false); this.debug = file.getBoolean("debug", false);
this.nameStyleColor = this.getNameStyleColor(file);
this.nameStyleDecorations = this.getNameStyleDecorations(file);
if (this.isConfigFileOutOfDate()) { if (this.isConfigFileOutOfDate()) {
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> { Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
@ -262,4 +279,28 @@ public class FakeplayerConfig extends PluginConfig {
return ConfigUtils.getEnum(file, "prevent-kicking", PreventKicking.class, PreventKicking.ON_SPAWNING); return ConfigUtils.getEnum(file, "prevent-kicking", PreventKicking.class, PreventKicking.ON_SPAWNING);
} }
private @NotNull NamedTextColor getNameStyleColor(@NotNull FileConfiguration file) {
var styles = Objects.requireNonNullElse(file.getString("name-style"), "").split(",\\s*");
var color = NamedTextColor.WHITE;
for (var style : styles) {
var c = NamedTextColor.NAMES.value(style);
if (c != null) {
color = c;
}
}
return color;
}
private @NotNull List<TextDecoration> getNameStyleDecorations(@NotNull FileConfiguration file) {
var styles = Objects.requireNonNullElse(file.getString("name-style"), "").split(",\\s*");
var decorations = new ArrayList<TextDecoration>();
for (var style : styles) {
var decoration = TextDecoration.NAMES.value(style);
if (decoration != null) {
decorations.add(decoration);
}
}
return decorations;
}
} }

View File

@ -17,6 +17,7 @@ import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName;
import io.github.hello09x.fakeplayer.core.util.Attributes; import io.github.hello09x.fakeplayer.core.util.Attributes;
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator; import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeInstance;
@ -36,7 +37,6 @@ import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.Component.translatable; import static net.kyori.adventure.text.Component.translatable;
import static net.kyori.adventure.text.format.NamedTextColor.*; import static net.kyori.adventure.text.format.NamedTextColor.*;
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
public class Fakeplayer { public class Fakeplayer {
@ -252,7 +252,7 @@ public class Fakeplayer {
} }
private void setupName() { private void setupName() {
var displayName = text(player.getName(), GRAY, ITALIC); var displayName = text(player.getName(), config.getNameStyleColor(), config.getNameStyleDecorations().toArray(new TextDecoration[0]));
player.playerListName(displayName); player.playerListName(displayName);
player.displayName(displayName); player.displayName(displayName);
player.customName(displayName); player.customName(displayName);

View File

@ -9,6 +9,7 @@ import io.github.hello09x.fakeplayer.core.repository.FakeplayerProfileRepository
import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository; import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -118,6 +119,9 @@ public class NameManager {
* @return 序列名 * @return 序列名
*/ */
public @NotNull SequenceName getSpecifiedName(@NotNull String name) { public @NotNull SequenceName getSpecifiedName(@NotNull String name) {
if (StringUtils.isNotBlank(config.getNamePrefix())) {
name = config.getNamePrefix().trim() + name;
}
if (name.startsWith("-")) { if (name.startsWith("-")) {
throw new IllegalCustomNameException(translatable( throw new IllegalCustomNameException(translatable(
"fakeplayer.spawn.error.name.start-with-illegal-character", "fakeplayer.spawn.error.name.start-with-illegal-character",
@ -184,6 +188,9 @@ public class NameManager {
source = creator.getName(); source = creator.getName();
} }
source = source.replace("%c", creator.getName()); source = source.replace("%c", creator.getName());
if (StringUtils.isNotBlank(config.getNamePrefix())) {
source = config.getNamePrefix().trim() + source;
}
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
var seq = nameSources.computeIfAbsent(source, ignored -> new NameSource(config.getPlayerLimit())).pop(); var seq = nameSources.computeIfAbsent(source, ignored -> new NameSource(config.getPlayerLimit())).pop();

View File

@ -1,10 +1,18 @@
# ========================================================================================================= # =========================================================================================================
# 【Important】【非常重要】 # 【Important】
# DO NOT EDIT config.temp.yml, copy it and rename to config.yml as your configuration file # DO NOT EDIT config.tmpl.yml !!!
# 不要直接修改 config.tmpl.yml 文件,将这份文件复制并重命名为 config.yml 来作为配置文件 # Copy and rename this file to config.yml as your configuration file.
# 【Important】【非常重要】 # Any changes to this file will be reverted.
# -------------------------------------------------------------------------------------------------------
# 【非常重要】
# 不要修改 config.tmpl.yml !!!
# 复制此文件并重命名为 config.yml 来作为你的配置文件
# 此文件的任何修改将会在服务器启动时被还原
# ========================================================================================================= # =========================================================================================================
version: 18
# DO NOT EDIT
# 不要修改
version: 19
# 多国语言配置 # 多国语言配置
# 可选项: en, zh, zh_cn, zh_tw, zh_hk # 可选项: en, zh, zh_cn, zh_tw, zh_hk
@ -60,10 +68,15 @@ lifespan: 0
# Tips: # Tips:
# 1. If this value contains characters other than alphabetic, numbers, and underscores, many vanilla commands will not be usable on them. # 1. If this value contains characters other than alphabetic, numbers, and underscores, many vanilla commands will not be usable on them.
# 2. Characters longer than 16 characters will be truncated # 2. Characters longer than 16 characters will be truncated
# 3. Can not start with '-' # 3. Cannot start with '-'
# 4. It's not recommended, players may give them RES privileges, or put some items into their inventory. # 4. It's not recommended, players may give them RES privileges, or put some items into their inventory.
name-template: '' name-template: ''
# 假人名称前缀
# 自动生成的名称或者玩家创建时指定名称都将会追加前缀
# Name prefix.
# Prefix will be added to the generated name or specified name
name-prefix: ''
# 假人自定义名称允许的字符 # 假人自定义名称允许的字符
# 格式: 正则表达式 # 格式: 正则表达式
@ -74,6 +87,14 @@ name-template: ''
# 3. 如果你改了正则表达式, 请确保它以 `^` 开头并且以 `$` 结尾 # 3. 如果你改了正则表达式, 请确保它以 `^` 开头并且以 `$` 结尾
name-pattern: '^[a-zA-Z0-9_]+$' name-pattern: '^[a-zA-Z0-9_]+$'
# 假人在 Tab 上的名称样式
# Name style on Tab listing
# Options:
# black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray,
# dark_gray, blue, green, aqua, red, light_purple, yellow, white
# obfuscated, bold, strikethrough, underlined, italic
name-style: gray, italic
# 防止假人被其他插件踢掉, 这个选项用来兼容一些插件因为某些问题而踢掉假人 # 防止假人被其他插件踢掉, 这个选项用来兼容一些插件因为某些问题而踢掉假人
# 可选项: # 可选项:

View File

@ -108,6 +108,12 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-v1_21_8</artifactId>
<version>${revision}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -197,6 +203,11 @@
<include name="fakeplayer-${revision}.jar"/> <include name="fakeplayer-${revision}.jar"/>
</fileset> </fileset>
</copy> </copy>
<copy tofile="../server-1.21.8/plugins/fakeplayer.jar">
<fileset dir="${project.build.directory}">
<include name="fakeplayer-${revision}.jar"/>
</fileset>
</copy>
</target> </target>
</configuration> </configuration>
<goals> <goals>

View File

@ -11,3 +11,5 @@ io.github.hello09x.fakeplayer.v1_21_4.spi.NMSBridgeImpl
io.github.hello09x.fakeplayer.v1_21_5.spi.NMSBridgeImpl io.github.hello09x.fakeplayer.v1_21_5.spi.NMSBridgeImpl
io.github.hello09x.fakeplayer.v1_21_6.spi.NMSBridgeImpl io.github.hello09x.fakeplayer.v1_21_6.spi.NMSBridgeImpl
io.github.hello09x.fakeplayer.v1_21_7.spi.NMSBridgeImpl io.github.hello09x.fakeplayer.v1_21_7.spi.NMSBridgeImpl
io.github.hello09x.fakeplayer.v1_21_8.spi.NMSBridgeImpl

View File

@ -40,7 +40,11 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
private boolean addChannel(@NotNull Player player, @NotNull String channel) { private boolean addChannel(@NotNull Player player, @NotNull String channel) {
try { try {
var method = player.getClass().getMethod("addChannel", String.class); var method = player.getClass().getMethod("addChannel", String.class);
return (boolean) method.invoke(player, channel); var ret = method.invoke(player, channel);
if (ret instanceof Boolean success) {
return success;
}
return true;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
return false; return false;
} }

106
fakeplayer-v1_21_8/pom.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>fakeplayer-v1_21_8</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<nms.version>1.21.8-R0.1-SNAPSHOT</nms.version>
</properties>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<scope>provided</scope>
</dependency>
<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>
<groupId>io.github.hello09x.fakeplayer</groupId>
<artifactId>fakeplayer-v1_21_6</artifactId>
<version>${revision}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${nms.version}</version>
<classifier>remapped-mojang</classifier>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>2.0.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:${nms.version}:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>
org.spigotmc:spigot:${nms.version}:jar:remapped-mojang
</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>
${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
</inputFile>
<srgIn>org.spigotmc:minecraft-server:${nms.version}:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:${nms.version}:jar:remapped-obf
</remappedDependencies>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,55 @@
package io.github.hello09x.fakeplayer.v1_21_8.spi;
import io.github.hello09x.fakeplayer.api.spi.*;
import io.github.hello09x.fakeplayer.core.Main;
import io.github.hello09x.fakeplayer.v1_21_6.spi.*;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.net.InetAddress;
import java.util.Set;
public class NMSBridgeImpl implements NMSBridge {
private final static Set<String> SUPPORTS = Set.of("1.21.8");
@Override
public @NotNull NMSEntity fromEntity(@NotNull Entity entity) {
return new NMSEntityImpl(entity);
}
@Override
public @NotNull NMSServer fromServer(@NotNull Server server) {
return new NMSServerImpl(server);
}
@Override
public @NotNull NMSServerLevel fromWorld(@NotNull World world) {
return new NMSServerLevelImpl(world);
}
@Override
public @NotNull NMSServerPlayer fromPlayer(@NotNull Player player) {
return new NMSServerPlayerImpl(player);
}
@Override
public @NotNull NMSNetwork createNetwork(@NotNull InetAddress address) {
return new NMSNetworkImpl(address);
}
@Override
public boolean isSupported() {
return SUPPORTS.contains(Bukkit.getMinecraftVersion());
}
@Override
public @NotNull ActionTicker createAction(@NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
return new ActionTickerImpl(Main.getInjector().getInstance(NMSBridge.class), player, action, setting);
}
}

View File

@ -27,12 +27,13 @@
<module>fakeplayer-v1_21_5</module> <module>fakeplayer-v1_21_5</module>
<module>fakeplayer-v1_21_6</module> <module>fakeplayer-v1_21_6</module>
<module>fakeplayer-v1_21_7</module> <module>fakeplayer-v1_21_7</module>
<module>fakeplayer-v1_21_8</module>
</modules> </modules>
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>0.3.17</revision> <revision>0.3.18</revision>
<detools.version>0.1.6-SNAPSHOT</detools.version> <detools.version>0.1.6-SNAPSHOT</detools.version>
</properties> </properties>