mirror of
https://github.com/tanyaofei/minecraft-fakeplayer.git
synced 2025-09-14 11:16:46 +08:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2e6a3a34d9 | ||
![]() |
07340d3218 | ||
![]() |
6a3d880f17 | ||
![]() |
f261e16a23 | ||
![]() |
d4bc411536 | ||
![]() |
07eaa9774a | ||
![]() |
62b6da6c81 | ||
![]() |
fe316696d4 | ||
![]() |
61d4ba297d | ||
![]() |
9035a895fe | ||
![]() |
a1252094d7 | ||
![]() |
8d47bb2d2a | ||
![]() |
cd71977716 | ||
![]() |
7997a36804 | ||
![]() |
f4f2cb6a5f | ||
![]() |
e2d5c3f4fc | ||
![]() |
699ceec691 | ||
![]() |
0aae327988 | ||
![]() |
c96787592d | ||
![]() |
b1a75c5fe2 | ||
![]() |
b472fe2938 | ||
![]() |
4d32b1ac5d | ||
![]() |
611acffee9 | ||
![]() |
9a405dd80b | ||
![]() |
3399ef9b26 | ||
![]() |
3d70e2e3c4 | ||
![]() |
1e1cf79513 |
26
.github/ISSUE_TEMPLATE/bug-report.md
vendored
26
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Clear title is helpful for searching / 简要的标题有利于检索'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug / 描述你的 bug 情况**
|
||||
A clear and concise description of what the bug is. / 简单描述这是一个什么 bug
|
||||
|
||||
**To Reproduce / 如何触发**
|
||||
Steps to reproduce the behavior: / 触发的步骤
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
4. ...
|
||||
|
||||
**Server information / 服务器信息**
|
||||
- Software / 核心: [e.g. Purpur 1.20.1]
|
||||
- Java: [e.g. 21]
|
||||
- Version / 版本 [e.g. 0.3.1]
|
||||
|
||||
**Additional context / 更多信息**
|
||||
Add any other context about the problem here. Such as log and plugin list / 添加更多有帮助的信息,如插件列表、日志
|
27
.github/ISSUE_TEMPLATE/bug-report_en.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug-report_en.md
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
4. ...
|
||||
|
||||
**Server information**
|
||||
- Software: [e.g. Purpur 1.20.1]
|
||||
- Java: [e.g. 21]
|
||||
- Fakeplayer Version: [e.g. 0.3.1]
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here. Such as log, plugin list or config files of other plugins **(hide sensitive content)**.
|
30
.github/ISSUE_TEMPLATE/bug-report_zh.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/bug-report_zh.md
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
name: Bug 修复
|
||||
about: 告诉我们程序存在的问题以协助完善 Fakeplayer
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**描述**
|
||||
|
||||
简单描述这是一个怎么样的 Bug, 导致了什么问题
|
||||
|
||||
|
||||
|
||||
**触发步骤**
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
4. ...
|
||||
|
||||
**服务器信息**
|
||||
- 核心: [e.g. Purpur 1.20.1]
|
||||
- Java: [e.g. 21]
|
||||
- Fakeplayer 版本: [e.g. 0.3.1]
|
||||
|
||||
**其他有帮助的信息**
|
||||
|
||||
提供一些有帮助的信息以协助我们定位问题,如插件清单、报错日志、或者插件的一些配置 **(请隐藏敏感信息)**
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Clear title is helpful for searching / 简要的标题有利于检索'
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
|
101
README.md
101
README.md
@ -8,11 +8,6 @@ This is a server side plugin inspired by [Carpet-Mod](https://github.com/gnembon
|
||||
|
||||
[Click me](https://youtu.be/NePaDz-P5nI) to visit a demo video.
|
||||
|
||||
## Buy me a coffee
|
||||
|
||||
If you like this plugin, buy me a coffee On [PayPal](https://paypal.me/hello09x) 🎉
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
+ Lets you spawn fake players who look like real to the server, they can keep chunk loading
|
||||
@ -24,62 +19,61 @@ If you like this plugin, buy me a coffee On [PayPal](https://paypal.me/hello09x)
|
||||
## Requirements
|
||||
|
||||
+ [Paper](https://papermc.io) or [Purpur](http://purpurmc.org) software
|
||||
+ [CommandAPI](https://commandapi.jorel.dev) Plugin
|
||||
|
||||
+ [CommandAPI](https://commandapi.jorel.dev) Plugin (Any version **except** `10.0.0`)
|
||||
|
||||
## Config file
|
||||
|
||||
**Fakeplayer only generates a template config file named `config.tmpl.yml`.**
|
||||
You need to rename this file to `config.yml` as your configuration file.
|
||||
This approach can let you preview new content when you are upgrading it.
|
||||
|
||||
[Click to visit file content](fakeplayer-core/src/main/resources/config.yml)
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description | Permission | Note |
|
||||
|---------------|-------------------------------------------|------------------------------|--------------------------------------------------------------------|
|
||||
| /fp spawn | Spawn a fake player | fakeplayer.command.spawn | |
|
||||
| /fp kill | Kill a fake player | fakeplayer.command.kill | |
|
||||
| /fp killall | Kill all fake players on the server | OP | |
|
||||
| /fp select | Select a fake player as default | fakeplayer.command.select | Appears only when player spawned more then 1 fake players |
|
||||
| /fp selection | View selected fake player | fakeplayer.command.selection | Appears only when player spawned more then 1 fake players |
|
||||
| /fp list | List spawned fake players | fakeplayer.command.list | |
|
||||
| /fp distance | Show distance to a fake player | fakeplayer.command.distance | |
|
||||
| /fp drop | Drop held item | fakeplayer.command.drop | |
|
||||
| /fp dropstack | Drop entire stack of the held item | fakeplayer.command.dropstack | |
|
||||
| /fp dropinv | Drop all items in the inventory | fakeplayer.command.dropinv | |
|
||||
| /fp skin | Copy skin from another player | fakeplayer.command.skin | 60 seconds cooldown if copy from a offline player |
|
||||
| /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 wakeup | Wake up | fakeplayer.command.wakeup | |
|
||||
| /fp status | Show player status | fakeplayer.command.status | |
|
||||
| /fp respawn | Respawn a dead fake player | fakeplayer.command.respawn | Appears only when server config does not kick on fake player death |
|
||||
| /fp tp | Teleport to a fake player | fakeplayer.command.tp | |
|
||||
| /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | |
|
||||
| /fp tps | Swap positions with fake player | fakeplayer.command.tps | |
|
||||
| /fp set | Change the configuration of a fake player | fakeplayer.command.set | |
|
||||
| /fp config | Change default configuration | fakeplayer.command.config | |
|
||||
| /fp expme | Transfer exp to you | fakeplayer.command.expme | |
|
||||
| /fp attack | Attack | fakeplayer.command.attack | |
|
||||
| /fp mine | Mine | fakeplayer.command.mine | |
|
||||
| /fp use | Use/Interact/Place | fakeplayer.command.use | |
|
||||
| /fp jump | Jump | fakeplayer.command.jump | |
|
||||
| /fp stop | Stop all actions | fakeplayer.command.stop | |
|
||||
| /fp turn | Turn around | fakeplayer.command.turn | |
|
||||
| /fp look | Look at specified location | fakeplayer.command.look | |
|
||||
| /fp move | Move | fakeplayer.command.mvoe | |
|
||||
| /fp ride | Ride | fakeplayer.command.ride | |
|
||||
| /fp sneak | Sneak | fakeplayer.command.sneak | |
|
||||
| /fp sprint | Sprinting | fakeplayer.command.sprint | |
|
||||
| /fp swap | Swap main and off-hand items | fakeplayer.command.swap | |
|
||||
| /fp hold | Hold corresponding hotbar item | fakeplayer.command.hold | |
|
||||
| /fp cmd | Execute command | fakeplayer.command.cmd | |
|
||||
| /fp reload | Reload config file | OP | |
|
||||
| Command | Description | Permission | Note |
|
||||
|---------------|-------------------------------------------|------------------------------|-----------------------------------------------------------------|
|
||||
| /fp spawn | Spawn a fake player | fakeplayer.command.spawn | |
|
||||
| /fp kill | Kill a fake player | fakeplayer.command.kill | |
|
||||
| /fp killall | Kill all fake players on the server | OP | |
|
||||
| /fp select | Select a fake player as default | fakeplayer.command.select | Available when player spawned more then 1 fake players |
|
||||
| /fp selection | View selected fake player | fakeplayer.command.selection | Available only when player spawned more then 1 fake players |
|
||||
| /fp list | List spawned fake players | fakeplayer.command.list | |
|
||||
| /fp distance | Show distance to a fake player | fakeplayer.command.distance | |
|
||||
| /fp drop | Drop held item | fakeplayer.command.drop | |
|
||||
| /fp dropstack | Drop entire stack of the held item | fakeplayer.command.dropstack | |
|
||||
| /fp dropinv | Drop all items in the inventory | fakeplayer.command.dropinv | |
|
||||
| /fp skin | Copy skin from another player | fakeplayer.command.skin | 60 seconds cooldown if copy from a offline player |
|
||||
| /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 wakeup | Wake up | fakeplayer.command.wakeup | |
|
||||
| /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 tp | Teleport to a fake player | fakeplayer.command.tp | |
|
||||
| /fp tphere | Teleport a fake player to you | fakeplayer.command.tphere | |
|
||||
| /fp tps | Swap positions with fake player | fakeplayer.command.tps | |
|
||||
| /fp set | Change the configuration of a fake player | fakeplayer.command.set | |
|
||||
| /fp config | Change default configuration | fakeplayer.command.config | |
|
||||
| /fp expme | Transfer exp to you | fakeplayer.command.expme | |
|
||||
| /fp attack | Attack | fakeplayer.command.attack | |
|
||||
| /fp mine | Mine | fakeplayer.command.mine | |
|
||||
| /fp use | Use/Interact/Place | fakeplayer.command.use | |
|
||||
| /fp jump | Jump | fakeplayer.command.jump | |
|
||||
| /fp stop | Stop all actions | fakeplayer.command.stop | |
|
||||
| /fp turn | Turn around | fakeplayer.command.turn | |
|
||||
| /fp look | Look at specified location | fakeplayer.command.look | |
|
||||
| /fp move | Move | fakeplayer.command.mvoe | |
|
||||
| /fp ride | Ride | fakeplayer.command.ride | |
|
||||
| /fp sneak | Sneak | fakeplayer.command.sneak | |
|
||||
| /fp sprint | Sprinting | fakeplayer.command.sprint | |
|
||||
| /fp swap | Swap main and off-hand items | fakeplayer.command.swap | |
|
||||
| /fp hold | Hold corresponding hotbar item | fakeplayer.command.hold | |
|
||||
| /fp cmd | Execute command | fakeplayer.command.cmd | |
|
||||
| /fp reload | Reload config file | OP | |
|
||||
|
||||
## Personal Configuration
|
||||
|
||||
Each player can configure his own configuration, it will take effect on the next spawning
|
||||
**Each player** can configure his **own** configuration, it will take effect on the next spawning
|
||||
|
||||
Command examples:
|
||||
|
||||
@ -90,13 +84,13 @@ Command examples:
|
||||
|--------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| collidable | Whether collision box is enabled |
|
||||
| invulnerable | Whether invincible mode is enabled |
|
||||
| wolverine | Whether super heal mode is enabled |
|
||||
| look_at_entity | Automatically look at nearby attackable entities (including players), can be combined with `attack` to automatically fight monsters |
|
||||
| pickup_items | Whether to pick up items |
|
||||
| skin | Whether to use your skin |
|
||||
| replenish | Whether to auto-replenish |
|
||||
| autofish | Whether to autofish |
|
||||
|
||||
|
||||
## Permissions
|
||||
|
||||
<details>
|
||||
@ -160,23 +154,27 @@ If your server does not restrict various player commands, you can use this direc
|
||||
</details>
|
||||
|
||||
## Placeholder Variables
|
||||
|
||||
+ `%fakeplayer_total%`: Total count of fake players
|
||||
+ `%fakeplayer_creator%`: The creator name of a fake player
|
||||
+ `%fakeplayer_actions`: Active actions of a fake player such as : `USE|ATTACK`
|
||||
|
||||
# Custom Translation
|
||||
|
||||
1. Create a `message` folder in `plugins/fakeplayer`
|
||||
2. Copy [this file](fakeplayer-core/src/main/resources/message/message.properties) to `message` folder
|
||||
3. Rename the file to `message_language_region.properties` such as `message_en_us.properties`
|
||||
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
|
||||
|
||||
**Make sure the translation file is encoding with UTF-8**
|
||||
**Make sure the translation file is encoded with UTF-8**
|
||||
|
||||
# FAQs
|
||||
|
||||
## 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
|
||||
# config.yml
|
||||
prevent-kicking: ALWAYS
|
||||
@ -200,6 +198,7 @@ self-commands:
|
||||
```
|
||||
|
||||
# Build Project
|
||||
|
||||
See the [introduction](./BUILD.md).
|
||||
|
||||
|
||||
|
11
README_zh.md
11
README_zh.md
@ -4,9 +4,6 @@
|
||||
|
||||
这个插件模拟出真实的玩家来保证区块的加载以及怪物的生成,生电服利器。
|
||||
|
||||
## 请我喝杯咖啡
|
||||
为爱发电不易,如果你有实力,不妨[请我喝杯奶茶](https://afdian.com/a/hello09x)以支持我长期更新下去。
|
||||
|
||||
### 支持版本
|
||||
|
||||
仅支持 `Paper` 及其下游如 (`Purpur`) 核心,所有测试均在 `Purpur` 进行,因此 `Purpur` 的兼容性最高
|
||||
@ -16,7 +13,6 @@
|
||||
+ 支持 `1.20`, `1.20.2`, `1.20.3`, `1.20.4`, `1.20.5`, `1.20.6`
|
||||
+ 支持 `1.21`
|
||||
|
||||
|
||||
## 特性
|
||||
|
||||
1. 你可以召唤假人来帮你保持区块加载、怪物刷新
|
||||
@ -25,13 +21,12 @@
|
||||
4. 你可以控制假人执行一些动作比如: 跳跃、攻击、进食、睡觉等等。不仅如此,你还可以将这些行为设置为周期性的。
|
||||
5. 发挥你的想象~
|
||||
|
||||
|
||||
## 前置插件:
|
||||
|
||||
- [CommandAPI](https://commandapi.jorel.dev)
|
||||
|
||||
- [CommandAPI](https://commandapi.jorel.dev) (>=9.5.0, <= 9.7.0)
|
||||
|
||||
## 配置文件
|
||||
|
||||
与其他插件不同,Fakeplayer 只会生成一份名为 `config.tmpl.yml` 的**模版**配置文件,你需要将它重命名为 `config.yml` 才能用作配置文件。这样的好处是升级的时候可以提前知道新的内容。
|
||||
|
||||
## 命令
|
||||
@ -160,6 +155,7 @@ _此外,假人是一个模拟玩家,因此可以被任何指令所识别比
|
||||
|----------------|-------------------------------------------|
|
||||
| collidable | 是否开启碰撞箱 |
|
||||
| invulnerable | 是否无敌模式 |
|
||||
| wolverine | 金刚狼超强再生模式 |
|
||||
| look_at_entity | 是否自动看向附近的可攻击的实体(包括玩家), 可以配合 `attack` 自动打怪 |
|
||||
| pickup_items | 是否能够拾取物品 |
|
||||
| skin | 是否使用你的皮肤 |
|
||||
@ -237,7 +233,6 @@ allow-commands:
|
||||
如果你的服务器 `spigot.yml` 里的 `bungeecord` 设置值为 `true`, 那么此插件将会进行兼容,
|
||||
只要玩家在任意一个服务器里游玩,即使切换服务器他创建的假人都不会触发`跟随下线
|
||||
|
||||
|
||||
# 构建项目
|
||||
|
||||
看这个[指引](./BUILD.md)
|
||||
|
@ -4,7 +4,11 @@ import com.google.inject.Singleton;
|
||||
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
|
||||
import dev.jorel.commandapi.executors.CommandArguments;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.entity.AbstractHorse;
|
||||
import org.bukkit.entity.Boat;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.minecart.RideableMinecart;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
@ -72,10 +76,10 @@ public class RideCommand extends AbstractCommand {
|
||||
public void rideVehicle(@NotNull CommandSender sender, @NotNull CommandArguments args) throws WrapperCommandSyntaxException {
|
||||
var fake = getFakeplayer(sender, args);
|
||||
var entity = fake.getNearbyEntities(4.5, 4.5, 4.5)
|
||||
.stream()
|
||||
.filter(e -> e instanceof Minecart || e instanceof Boat || e instanceof AbstractHorse)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
.stream()
|
||||
.filter(e -> e instanceof RideableMinecart || e instanceof Boat || e instanceof AbstractHorse)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
|
@ -6,12 +6,12 @@ import dev.jorel.commandapi.executors.CommandArguments;
|
||||
import io.github.hello09x.devtools.core.utils.ExperienceUtils;
|
||||
import io.github.hello09x.fakeplayer.core.command.Permission;
|
||||
import io.github.hello09x.fakeplayer.core.repository.model.Feature;
|
||||
import io.github.hello09x.fakeplayer.core.util.Attributes;
|
||||
import io.github.hello09x.fakeplayer.core.util.Mth;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeInstance;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -87,7 +87,7 @@ public class StatusCommand extends AbstractCommand {
|
||||
|
||||
private @NotNull Component getHealthLine(@NotNull Player target) {
|
||||
var health = target.getHealth();
|
||||
double max = Optional.ofNullable(target.getAttribute(Attribute.GENERIC_MAX_HEALTH))
|
||||
double max = Optional.ofNullable(target.getAttribute(Attributes.maxHealth()))
|
||||
.map(AttributeInstance::getValue)
|
||||
.orElse(20D);
|
||||
|
||||
|
@ -10,16 +10,15 @@ import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.repository.model.Feature;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@ -37,8 +36,6 @@ public class FakeplayerConfig extends PluginConfig {
|
||||
|
||||
private final static String defaultNameChars = "^[a-zA-Z0-9_]+$";
|
||||
|
||||
public final static String SECTION_KEY_DEFAULT_FEATURES = "default-features";
|
||||
|
||||
/**
|
||||
* 每位玩家最多多少个假人
|
||||
*/
|
||||
@ -54,6 +51,21 @@ public class FakeplayerConfig extends PluginConfig {
|
||||
*/
|
||||
private String nameTemplate;
|
||||
|
||||
/**
|
||||
* 假人名称前缀
|
||||
*/
|
||||
private String namePrefix;
|
||||
|
||||
/**
|
||||
* 名称样式, 颜色
|
||||
*/
|
||||
private NamedTextColor nameStyleColor;
|
||||
|
||||
/**
|
||||
* 名称样式, 格式
|
||||
*/
|
||||
private List<TextDecoration> nameStyleDecorations;
|
||||
|
||||
/**
|
||||
* 创建者玩家下线时是否跟随下线
|
||||
*/
|
||||
@ -189,6 +201,7 @@ public class FakeplayerConfig extends PluginConfig {
|
||||
this.namePattern = getNamePattern(file);
|
||||
this.preventKicking = this.getPreventKicking(file);
|
||||
this.nameTemplate = getNameTemplate(file);
|
||||
this.namePrefix = file.getString("name-prefix", "");
|
||||
this.lifespan = getLifespan(file);
|
||||
this.allowCommands = file.getStringList("allow-commands")
|
||||
.stream()
|
||||
@ -201,6 +214,8 @@ public class FakeplayerConfig extends PluginConfig {
|
||||
.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.debug = file.getBoolean("debug", false);
|
||||
this.nameStyleColor = this.getNameStyleColor(file);
|
||||
this.nameStyleDecorations = this.getNameStyleDecorations(file);
|
||||
|
||||
if (this.isConfigFileOutOfDate()) {
|
||||
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> {
|
||||
@ -264,4 +279,28 @@ public class FakeplayerConfig extends PluginConfig {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,11 +14,12 @@ import io.github.hello09x.fakeplayer.core.manager.FakeplayerReplenishManager;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerSkinManager;
|
||||
import io.github.hello09x.fakeplayer.core.manager.action.ActionManager;
|
||||
import io.github.hello09x.fakeplayer.core.manager.naming.SequenceName;
|
||||
import io.github.hello09x.fakeplayer.core.util.Attributes;
|
||||
import io.github.hello09x.fakeplayer.core.util.InternalAddressGenerator;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeInstance;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -36,7 +37,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.Component.translatable;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.*;
|
||||
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
|
||||
|
||||
public class Fakeplayer {
|
||||
|
||||
@ -165,9 +165,10 @@ public class Fakeplayer {
|
||||
|
||||
this.network = bridge.createNetwork(address);
|
||||
this.network.placeNewPlayer(Bukkit.getServer(), this.player);
|
||||
this.player.setHealth(Optional.ofNullable(this.player.getAttribute(Attribute.GENERIC_MAX_HEALTH))
|
||||
this.player.setHealth(Optional.ofNullable(this.player.getAttribute(Attributes.maxHealth()))
|
||||
.map(AttributeInstance::getValue)
|
||||
.orElse(20D)); // 恢复生命值
|
||||
this.player.setFoodLevel(20);
|
||||
this.setupName();
|
||||
this.handle.setupClientOptions(); // 处理皮肤设置问题
|
||||
|
||||
@ -251,7 +252,7 @@ public class Fakeplayer {
|
||||
}
|
||||
|
||||
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.displayName(displayName);
|
||||
player.customName(displayName);
|
||||
|
@ -28,7 +28,9 @@ public record SpawnOption(
|
||||
|
||||
boolean replenish,
|
||||
|
||||
boolean autofish
|
||||
boolean autofish,
|
||||
|
||||
boolean wolverine
|
||||
|
||||
) {
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
package io.github.hello09x.fakeplayer.core.entity.action.impl;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import io.github.hello09x.fakeplayer.api.spi.Action;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
import org.bukkit.entity.Damageable;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class LookAtEntityAction implements Action {
|
||||
|
||||
@ -26,7 +30,12 @@ public class LookAtEntityAction implements Action {
|
||||
return false;
|
||||
}
|
||||
|
||||
bukkitPlayer.lookAt(Iterables.getFirst(entities, null), LookAnchor.EYES, LookAnchor.EYES);
|
||||
var entity = this.getNearestEntity(bukkitPlayer, entities);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bukkitPlayer.lookAt(entity, LookAnchor.EYES, LookAnchor.EYES);
|
||||
player.resetLastActionTime();
|
||||
return true;
|
||||
}
|
||||
@ -40,4 +49,25 @@ public class LookAtEntityAction implements Action {
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
private @Nullable Entity getNearestEntity(@NotNull Player player, @NotNull Collection<? extends Entity> entities) {
|
||||
if (entities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
var loc = player.getLocation();
|
||||
Entity nearest = null;
|
||||
double distance = 0;
|
||||
for (var entity : entities) {
|
||||
if (nearest == null) {
|
||||
nearest = entity;
|
||||
} else {
|
||||
var d = loc.distance(entity.getLocation());
|
||||
if (d < distance) {
|
||||
nearest = entity;
|
||||
distance = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ public class FakeplayerListener implements Listener {
|
||||
|
||||
// 有一些跨服同步插件会退出时同步生命值, 假人重新生成的时候同步为 0
|
||||
// 因此在死亡时将生命值设置恢复满血先
|
||||
Optional.ofNullable(player.getAttribute(Attribute.GENERIC_MAX_HEALTH))
|
||||
Optional.ofNullable(player.getAttribute(Attribute.MAX_HEALTH))
|
||||
.map(AttributeInstance::getValue)
|
||||
.ifPresent(player::setHealth);
|
||||
event.setCancelled(true);
|
||||
|
@ -67,14 +67,14 @@ public class FakeplayerManager {
|
||||
|
||||
this.lagMonitor = Executors.newSingleThreadScheduledExecutor();
|
||||
this.lagMonitor.scheduleWithFixedDelay(() -> {
|
||||
if (Bukkit.getServer().getTPS()[1] < config.getKaleTps()) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
if (this.removeAll("low tps") > 0) {
|
||||
Bukkit.broadcast(translatable("fakeplayer.manager.remove-all-on-low-tps", GRAY, ITALIC));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0, 60, TimeUnit.SECONDS
|
||||
if (Bukkit.getServer().getTPS()[1] < config.getKaleTps()) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
if (this.removeAll("low tps") > 0) {
|
||||
Bukkit.broadcast(translatable("fakeplayer.manager.remove-all-on-low-tps", GRAY, ITALIC));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0, 60, TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
@ -117,7 +117,8 @@ public class FakeplayerManager {
|
||||
configs.get(Feature.pickup_items).asBoolean(),
|
||||
configs.get(Feature.skin).asBoolean(),
|
||||
configs.get(Feature.replenish).asBoolean(),
|
||||
configs.get(Feature.autofish).asBoolean()
|
||||
configs.get(Feature.autofish).asBoolean(),
|
||||
configs.get(Feature.wolverine).asBoolean()
|
||||
);
|
||||
})
|
||||
.thenComposeAsync(fp::spawnAsync)
|
||||
|
@ -9,6 +9,7 @@ import io.github.hello09x.fakeplayer.core.repository.FakeplayerProfileRepository
|
||||
import io.github.hello09x.fakeplayer.core.repository.UsedIdRepository;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -118,6 +119,9 @@ public class NameManager {
|
||||
* @return 序列名
|
||||
*/
|
||||
public @NotNull SequenceName getSpecifiedName(@NotNull String name) {
|
||||
if (StringUtils.isNotBlank(config.getNamePrefix())) {
|
||||
name = config.getNamePrefix().trim() + name;
|
||||
}
|
||||
if (name.startsWith("-")) {
|
||||
throw new IllegalCustomNameException(translatable(
|
||||
"fakeplayer.spawn.error.name.start-with-illegal-character",
|
||||
@ -184,6 +188,9 @@ public class NameManager {
|
||||
source = 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++) {
|
||||
var seq = nameSources.computeIfAbsent(source, ignored -> new NameSource(config.getPlayerLimit())).pop();
|
||||
|
@ -8,10 +8,13 @@ import lombok.Getter;
|
||||
import net.kyori.adventure.translation.Translatable;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -42,17 +45,35 @@ public enum Feature implements Translatable, Singletons {
|
||||
"fakeplayer.config.invulnerable",
|
||||
List.of(Permission.config),
|
||||
List.of("true", "false"),
|
||||
"true",
|
||||
"false",
|
||||
faker -> String.valueOf(faker.isInvulnerable()),
|
||||
(faker, value) -> faker.setInvulnerable(Boolean.parseBoolean(value))
|
||||
),
|
||||
|
||||
/**
|
||||
* 金刚狼模式 -> 超强的再生能力
|
||||
*/
|
||||
wolverine(
|
||||
"fakeplayer.config.wolverine",
|
||||
List.of(Permission.config),
|
||||
List.of("true", "false"),
|
||||
"false",
|
||||
fake -> String.valueOf(Optional.ofNullable(fake.getPotionEffect(PotionEffectType.REGENERATION)).map(PotionEffect::isInfinite).orElse(false)),
|
||||
(faker, value) -> {
|
||||
if (Boolean.parseBoolean(value)) {
|
||||
faker.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, PotionEffect.INFINITE_DURATION, 4, true, true));
|
||||
} else {
|
||||
faker.removePotionEffect(PotionEffectType.REGENERATION);
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
/**
|
||||
* 是否自动看向实体
|
||||
*/
|
||||
look_at_entity(
|
||||
"fakeplayer.config.look_at_entity",
|
||||
List.of(Permission.config),
|
||||
List.of(Permission.config, Permission.look),
|
||||
List.of("true", "false"),
|
||||
"false",
|
||||
faker -> String.valueOf(actionManager.get().hasActiveAction(faker, ActionType.LOOK_AT_NEAREST_ENTITY)),
|
||||
|
@ -0,0 +1,35 @@
|
||||
package io.github.hello09x.fakeplayer.core.util;
|
||||
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* @author tanyaofei
|
||||
* @since 2025/7/16
|
||||
**/
|
||||
public abstract class Attributes {
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li><= 1.12.5 GENERIC_MAX_HEALTH</li>
|
||||
* <li>>= 1.21.6 MAX_HEALTH</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static @NotNull Attribute maxHealth() {
|
||||
try {
|
||||
return (Attribute) Attribute.class.getField("GENERIC_MAX_HEALTH").get(Attribute.class);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
try {
|
||||
return (Attribute) Attribute.class.getField("MAX_HEALTH").get(Attribute.class);
|
||||
} catch (Exception e1) {
|
||||
throw new NoSuchElementException("No attribute found for MAX_HEALTH or GENERIC_MAX_HEALTH");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,10 +1,18 @@
|
||||
# =========================================================================================================
|
||||
# 【Important】【非常重要】
|
||||
# DO NOT EDIT config.temp.yml, copy it and rename to config.yml as your configuration file
|
||||
# 不要直接修改 config.tmpl.yml 文件,将这份文件复制并重命名为 config.yml 来作为配置文件
|
||||
# 【Important】【非常重要】
|
||||
# 【Important】
|
||||
# DO NOT EDIT config.tmpl.yml !!!
|
||||
# Copy and rename this file to config.yml as your configuration file.
|
||||
# Any changes to this file will be reverted.
|
||||
# -------------------------------------------------------------------------------------------------------
|
||||
# 【非常重要】
|
||||
# 不要修改 config.tmpl.yml !!!
|
||||
# 复制此文件并重命名为 config.yml 来作为你的配置文件
|
||||
# 此文件的任何修改将会在服务器启动时被还原
|
||||
# =========================================================================================================
|
||||
version: 17
|
||||
|
||||
# DO NOT EDIT
|
||||
# 不要修改
|
||||
version: 19
|
||||
|
||||
# 多国语言配置
|
||||
# 可选项: en, zh, zh_cn, zh_tw, zh_hk
|
||||
@ -60,10 +68,15 @@ lifespan: 0
|
||||
# Tips:
|
||||
# 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
|
||||
# 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.
|
||||
name-template: ''
|
||||
|
||||
# 假人名称前缀
|
||||
# 自动生成的名称或者玩家创建时指定名称都将会追加前缀
|
||||
# Name prefix.
|
||||
# Prefix will be added to the generated name or specified name
|
||||
name-prefix: ''
|
||||
|
||||
# 假人自定义名称允许的字符
|
||||
# 格式: 正则表达式
|
||||
@ -74,6 +87,14 @@ name-template: ''
|
||||
# 3. 如果你改了正则表达式, 请确保它以 `^` 开头并且以 `$` 结尾
|
||||
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
|
||||
|
||||
|
||||
# 防止假人被其他插件踢掉, 这个选项用来兼容一些插件因为某些问题而踢掉假人
|
||||
# 可选项:
|
||||
@ -264,6 +285,7 @@ default-features:
|
||||
invulnerable: false
|
||||
replenish: false
|
||||
autofish: false
|
||||
wolverine: false
|
||||
|
||||
|
||||
# 检测更新
|
||||
|
@ -149,5 +149,6 @@ fakeplayer.spawn.error.name.too-short=Requires a name has more than {0} characte
|
||||
fakeplayer.spawn.error.name.used=Name {0} was used by a real player({1})
|
||||
fakeplayer.command.ride.entity.error.too-far=Entity is too far away from {0}
|
||||
fakeplayer.command.ride.entity.description=Ride specified entity
|
||||
fakeplayer.config.wolverine=Super heal
|
||||
|
||||
|
||||
|
@ -149,5 +149,6 @@ fakeplayer.spawn.error.name.too-short=\u540D\u79F0\u6700\u5C11 {0} \u4E2A\u5B57\
|
||||
fakeplayer.spawn.error.name.used=\u540D\u79F0 {0} \u88AB\u771F\u5B9E\u73A9\u5BB6({1})\u4F7F\u7528\u8FC7\u4E86
|
||||
fakeplayer.command.ride.entity.error.too-far=\u6307\u5B9A\u7684\u5B9E\u4F53\u79BB {0} \u592A\u8FDC\u4E86
|
||||
fakeplayer.command.ride.entity.description=\u9A91\u6307\u5B9A\u5B9E\u4F53
|
||||
fakeplayer.config.wolverine=\u8D85\u5F3A\u518D\u751F
|
||||
|
||||
|
||||
|
@ -148,4 +148,5 @@ fakeplayer.spawn.error.name.too-long=\u540D\u7A31\u81F3\u591A {0} \u500B\u5B57
|
||||
fakeplayer.spawn.error.name.too-short=\u540D\u7A31\u81F3\u5C11 {0} \u500B\u5B57
|
||||
fakeplayer.spawn.error.name.used=\u540D\u7A31 {0} \u88AB\u771F\u5BE6\u73A9\u5BB6({1})\u4F7F\u7528\u904E\u4E86
|
||||
fakeplayer.command.ride.entity.error.too-far=\u6307\u5B9A\u7684\u5BE6\u9AD4\u96E2 {0} \u592A\u9060\u4E86
|
||||
fakeplayer.command.ride.entity.description=\u9A0E\u6307\u5B9A\u5BE6\u9AD4
|
||||
fakeplayer.command.ride.entity.description=\u9A0E\u6307\u5B9A\u5BE6\u9AD4
|
||||
fakeplayer.config.wolverine=\u8D85\u5F37\u518D\u751F
|
@ -148,4 +148,5 @@ fakeplayer.spawn.error.name.too-long=\u540D\u7A31\u6700\u591A {0} \u500B\u5B57\u
|
||||
fakeplayer.spawn.error.name.too-short=\u540D\u7A31\u6700\u5C11 {0} \u500B\u5B57\u7B26
|
||||
fakeplayer.spawn.error.name.used=\u540D\u7A31 {0} \u88AB\u771F\u5BE6\u73A9\u5BB6({1})\u4F7F\u7528\u904E\u4E86
|
||||
fakeplayer.command.ride.entity.error.too-far=\u6307\u5B9A\u7684\u5BE6\u9AD4\u96E2 {0} \u592A\u9060\u4E86
|
||||
fakeplayer.command.ride.entity.description=\u9A0E\u6307\u5B9A\u5BE6\u9AD4
|
||||
fakeplayer.command.ride.entity.description=\u9A0E\u6307\u5B9A\u5BE6\u9AD4
|
||||
fakeplayer.config.wolverine=\u8D85\u5F37\u518D\u751F
|
@ -78,6 +78,42 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_3</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_4</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_5</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_6</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_7</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.hello09x.fakeplayer</groupId>
|
||||
<artifactId>fakeplayer-v1_21_8</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -142,6 +178,36 @@
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.3/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.4/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.5/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.6/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.7/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy tofile="../server-1.21.8/plugins/fakeplayer.jar">
|
||||
<fileset dir="${project.build.directory}">
|
||||
<include name="fakeplayer-${revision}.jar"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
|
@ -6,3 +6,10 @@ io.github.hello09x.fakeplayer.v1_20_5.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_20_6.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_21.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_21_1.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_21_3.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_21_4.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_7.spi.NMSBridgeImpl
|
||||
io.github.hello09x.fakeplayer.v1_21_8.spi.NMSBridgeImpl
|
||||
|
||||
|
@ -6,7 +6,6 @@ import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
@ -35,21 +34,9 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
// 接收到自定义的数据包,由于假人没有连接导致 BungeeCord 的插件消息无法正确通过 Proxy 发送
|
||||
// 因此将该数据包通过真实的玩家重新发送一份
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
@ -40,20 +39,6 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p.payload());
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
@ -36,24 +35,10 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
|
||||
.ifPresent(p -> p.addChannel(BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p.payload());
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
@ -36,24 +35,10 @@ public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerIm
|
||||
.ifPresent(p -> p.addChannel(BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
100
fakeplayer-v1_21_3/pom.xml
Normal file
100
fakeplayer-v1_21_3/pom.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?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_3</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.3-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>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>${nms.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<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>
|
@ -0,0 +1,49 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.action;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
|
||||
public class AttackAction extends TraceAction {
|
||||
|
||||
private final ServerPlayer player;
|
||||
|
||||
public AttackAction(ServerPlayer player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.ENTITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
player.attack(entityHit.getEntity());
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
player.resetAttackStrengthTicker();
|
||||
player.resetLastActionTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*;
|
||||
|
||||
public class MineAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public MineAction(ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.BLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
|
||||
if (player.blockActionRestricted(player.level(), pos, player.gameMode.getGameModeForPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.pos != null && player.level().getBlockState(current.pos).isAir()) {
|
||||
current.pos = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var state = player.level().getBlockState(pos);
|
||||
var broken = false;
|
||||
if (player.gameMode.getGameModeForPlayer().isCreative()) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
} else if (current.pos == null || !current.pos.equals(pos)) {
|
||||
if (current.pos != null) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
|
||||
if (!state.isAir() && current.progress == 0) {
|
||||
state.attack(player.level(), pos, player);
|
||||
}
|
||||
|
||||
if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) {
|
||||
current.pos = null;
|
||||
broken = true;
|
||||
} else {
|
||||
current.pos = pos;
|
||||
current.progress = 0;
|
||||
}
|
||||
} else {
|
||||
current.progress += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (current.progress >= 1) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
STOP_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
}
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (current.progress * 10));
|
||||
}
|
||||
|
||||
player.resetLastActionTime();
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
return broken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public void stop() {
|
||||
if (current.pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.level().destroyBlockProgress(-1, current.pos, -1);
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
Direction.DOWN,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 0;
|
||||
current.progress = 0;
|
||||
}
|
||||
|
||||
private static class Current {
|
||||
|
||||
/**
|
||||
* 当前左键的目标位置
|
||||
*/
|
||||
@Nullable
|
||||
public BlockPos pos;
|
||||
|
||||
/**
|
||||
* 破坏方块的进度
|
||||
*/
|
||||
public float progress;
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.action;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.Action;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.action.util.Tracer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class TraceAction implements Action {
|
||||
|
||||
protected final ServerPlayer player;
|
||||
|
||||
protected TraceAction(@NotNull ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
protected @Nullable HitResult getTarget() {
|
||||
double reach = player.gameMode.isCreative() ? 5 : 4.5f;
|
||||
return Tracer.rayTrace(player, 1, reach, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.action;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class UseAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public UseAction(@NotNull ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isUsingItem()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var hand : InteractionHand.values()) {
|
||||
switch (hit.getType()) {
|
||||
case BLOCK -> {
|
||||
player.resetLastActionTime();
|
||||
var world = player.serverLevel();
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
if (pos.getY() < player.level().getMaxY() - (side == Direction.UP ? 1 : 0) && world.mayInteract(player, pos)) {
|
||||
var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit);
|
||||
if (result.consumesAction()) {
|
||||
player.swing(hand);
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
player.resetLastActionTime();
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
var entity = entityHit.getEntity();
|
||||
boolean handWasEmpty = player.getItemInHand(hand).isEmpty();
|
||||
boolean itemFrameEmpty = (entity instanceof ItemFrame) && ((ItemFrame) entity).getItem().isEmpty();
|
||||
var pos = entityHit.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
if (entity.interactAt(player, pos, hand).consumesAction()) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handItem = player.getItemInHand(hand);
|
||||
if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) {
|
||||
player.resetLastActionTime();
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
current.freeze = 0;
|
||||
player.releaseUsingItem();
|
||||
}
|
||||
|
||||
private final static class Current {
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.action.util;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* copy from fabric carpet mod
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
public static @Nullable HitResult rayTrace(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var blockHit = rayTraceBlocks(source, partialTicks, reach, fluids);
|
||||
double maxSqDist = reach * reach;
|
||||
if (blockHit != null) {
|
||||
maxSqDist = blockHit.getLocation().distanceToSqr(source.getEyePosition(partialTicks));
|
||||
}
|
||||
EntityHitResult entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist);
|
||||
return entityHit == null ? blockHit : entityHit;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static @Nullable BlockHitResult rayTraceBlocks(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var rotation = source.getViewVector(partialTicks);
|
||||
var reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach);
|
||||
return source.level().clip(new ClipContext(pos, reachEnd, ClipContext.Block.OUTLINE, fluids ?
|
||||
ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, source));
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
double maxSqDist
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var reachVec = source.getViewVector(partialTicks).scale(reach);
|
||||
var box = source.getBoundingBox().expandTowards(reachVec).inflate(1);
|
||||
return rayTraceEntities(source,
|
||||
pos,
|
||||
pos.add(reachVec),
|
||||
box,
|
||||
e -> !e.isSpectator() && e.isPickable(),
|
||||
maxSqDist);
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
@NotNull Vec3 start,
|
||||
@NotNull Vec3 end,
|
||||
@NotNull AABB box,
|
||||
@NotNull Predicate<Entity> predicate,
|
||||
double maxSqDistance
|
||||
) {
|
||||
@SuppressWarnings("resource")
|
||||
var world = source.level();
|
||||
double targetDistance = maxSqDistance;
|
||||
Entity target = null;
|
||||
Vec3 targetHitPos = null;
|
||||
for (Entity current : world.getEntities(source, box, predicate)) {
|
||||
var currentBox = current.getBoundingBox().inflate(current.getPickRadius());
|
||||
var currentHit = currentBox.clip(start, end);
|
||||
if (currentBox.contains(start)) {
|
||||
if (targetDistance >= 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHit.orElse(start);
|
||||
targetDistance = 0;
|
||||
}
|
||||
} else if (currentHit.isPresent()) {
|
||||
var currentHitPos = currentHit.get();
|
||||
var currentDistance = start.distanceToSqr(currentHitPos);
|
||||
if (currentDistance < targetDistance || targetDistance == 0) {
|
||||
if (current.getRootVehicle() == source.getRootVehicle()) {
|
||||
if (targetDistance == 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
targetDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target == null ? null : new EntityHitResult(target, targetHitPos);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import io.github.hello09x.fakeplayer.core.network.FakeChannel;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.PacketSendListener;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeConnection extends Connection {
|
||||
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
|
||||
public FakeConnection(@NotNull InetAddress address) {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
this.channel = new FakeChannel(null, address);
|
||||
this.address = this.channel.remoteAddress();
|
||||
Connection.configureSerialization(this.channel.pipeline(), PacketFlow.SERVERBOUND, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.network;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.ServerAdvancementManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FakePlayerAdvancements extends PlayerAdvancements {
|
||||
|
||||
public FakePlayerAdvancements(
|
||||
DataFixer datafixer,
|
||||
PlayerList playerlist,
|
||||
ServerAdvancementManager manager,
|
||||
Path path,
|
||||
ServerPlayer player
|
||||
) {
|
||||
super(datafixer, playerlist, manager, path, player);
|
||||
this.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean award(AdvancementHolder advancementholder, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushDirty(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
|
||||
return new AdvancementProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revoke(AdvancementHolder advancement, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedTab(AdvancementHolder advancement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopListening() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
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.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerImpl implements NMSServerGamePacketListener {
|
||||
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
|
||||
public FakeServerGamePacketListenerImpl(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull Connection connection,
|
||||
@NotNull ServerPlayer player,
|
||||
@NotNull CommonListenerCookie cookie
|
||||
) {
|
||||
super(server, connection, player, cookie);
|
||||
Optional.ofNullable(Bukkit.getPlayer(player.getUUID()))
|
||||
.map(CraftPlayer.class::cast)
|
||||
.ifPresent(p -> p.addChannel(BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCustomPayloadPacket(@NotNull ClientboundCustomPayloadPacket packet) {
|
||||
var payload = packet.payload();
|
||||
var resourceLocation = payload.type().id();
|
||||
var channel = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
|
||||
if (!channel.equals(BUNGEE_CORD_CORRECTED_CHANNEL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(payload instanceof DiscardedPayload p)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recipient = Bukkit
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(manager::isNotFake)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if (recipient == null) {
|
||||
log.warning("Failed to forward a plugin message cause non real players in the server");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = p.data().array();
|
||||
recipient.sendPluginMessage(Main.getInstance(), BUNGEE_CORD_CHANNEL, message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionSetting;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionTicker;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionType;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
|
||||
import io.github.hello09x.fakeplayer.core.entity.action.BaseActionTicker;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.action.AttackAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.action.MineAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.action.UseAction;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ActionTickerImpl extends BaseActionTicker implements ActionTicker {
|
||||
|
||||
public ActionTickerImpl(@NotNull NMSBridge nms, @NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
|
||||
super(nms, player, action, setting);
|
||||
if (this.action == null) {
|
||||
this.action = switch (action) {
|
||||
case ATTACK -> new AttackAction(((CraftPlayer) player).getHandle());
|
||||
case MINE -> new MineAction(((CraftPlayer) player).getHandle());
|
||||
case USE -> new UseAction(((CraftPlayer) player).getHandle());
|
||||
case JUMP, LOOK_AT_NEAREST_ENTITY, DROP_INVENTORY, DROP_STACK, DROP_ITEM ->
|
||||
throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.*;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
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.2", "1.21.3");
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSEntity;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSEntityImpl implements NMSEntity {
|
||||
|
||||
@Getter
|
||||
private final Entity handle;
|
||||
|
||||
public NMSEntityImpl(@NotNull org.bukkit.entity.@NotNull Entity entity) {
|
||||
this.handle = ((CraftEntity) entity).getHandle();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.network.FakeConnection;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.network.FakeServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class NMSNetworkImpl implements NMSNetwork {
|
||||
|
||||
@NotNull
|
||||
private final FakeConnection connection;
|
||||
|
||||
private NMSServerGamePacketListener serverGamePacketListener;
|
||||
|
||||
public NMSNetworkImpl(
|
||||
@NotNull InetAddress address
|
||||
) {
|
||||
this.connection = new FakeConnection(address);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener placeNewPlayer(
|
||||
@NotNull Server server,
|
||||
@NotNull Player player
|
||||
) {
|
||||
var handle = ((CraftPlayer) player).getHandle();
|
||||
var cookie = CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile(), false);
|
||||
|
||||
((CraftServer) server).getHandle().placeNewPlayer(
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
|
||||
var listener = new FakeServerGamePacketListenerImpl(
|
||||
((CraftServer) server).getServer(),
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
this.serverGamePacketListener = listener;
|
||||
handle.connection = listener;
|
||||
return listener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener getServerGamePacketListener() throws IllegalStateException {
|
||||
if (this.serverGamePacketListener == null) {
|
||||
throw new IllegalStateException("not initialized");
|
||||
}
|
||||
return this.serverGamePacketListener;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.github.hello09x.devtools.core.utils.WorldUtils;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServer;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class NMSServerImpl implements NMSServer {
|
||||
|
||||
|
||||
@Getter
|
||||
private final MinecraftServer handle;
|
||||
|
||||
public NMSServerImpl(@NotNull Server server) {
|
||||
this.handle = ((CraftServer) server).getServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NMSServerPlayer newPlayer(@NotNull UUID uuid, @NotNull String name) {
|
||||
var handle = new ServerPlayer(
|
||||
new NMSServerImpl(Bukkit.getServer()).getHandle(),
|
||||
new NMSServerLevelImpl(WorldUtils.getMainWorld()).getHandle(),
|
||||
new GameProfile(uuid, name),
|
||||
ClientInformation.createDefault()
|
||||
);
|
||||
return new NMSServerPlayerImpl(handle.getBukkitEntity());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerLevel;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSServerLevelImpl implements NMSServerLevel {
|
||||
|
||||
@Getter
|
||||
private final ServerLevel handle;
|
||||
|
||||
public NMSServerLevelImpl(@NotNull World world) {
|
||||
this.handle = ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_3.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import io.github.hello09x.fakeplayer.core.constant.ConstantPool;
|
||||
import io.github.hello09x.fakeplayer.core.util.Reflections;
|
||||
import io.github.hello09x.fakeplayer.v1_21_3.network.FakePlayerAdvancements;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientCommandPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class NMSServerPlayerImpl implements NMSServerPlayer {
|
||||
|
||||
private final static Field ServerPlayer$advancements = Reflections.getFirstFieldByType(
|
||||
ServerPlayer.class,
|
||||
PlayerAdvancements.class,
|
||||
false
|
||||
);
|
||||
|
||||
@Getter
|
||||
private final ServerPlayer handle;
|
||||
|
||||
@Getter
|
||||
private final CraftPlayer player;
|
||||
|
||||
public NMSServerPlayerImpl(@NotNull Player player) {
|
||||
this.player = ((CraftPlayer) player);
|
||||
this.handle = ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return handle.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return handle.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return handle.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXo(double xo) {
|
||||
handle.xo = xo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYo(double yo) {
|
||||
handle.yo = yo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZo(double zo) {
|
||||
handle.zo = zo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
handle.doTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absMoveTo(double x, double y, double z, float yRot, float xRot) {
|
||||
handle.absMoveTo(x, y, z, yRot, xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYRot() {
|
||||
return handle.getYRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYRot(float yRot) {
|
||||
handle.setYRot(yRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXRot() {
|
||||
return handle.getXRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRot(float xRot) {
|
||||
handle.setXRot(xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZza() {
|
||||
return handle.zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZza(float zza) {
|
||||
handle.zza = zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXxa() {
|
||||
return handle.xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXxa(float xxa) {
|
||||
handle.xxa = xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeltaMovement(@NotNull Vector vector) {
|
||||
handle.setDeltaMovement(new Vec3(
|
||||
vector.getX(),
|
||||
vector.getY(),
|
||||
vector.getZ()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(@NotNull Entity entity, boolean force) {
|
||||
return handle.startRiding(new NMSEntityImpl(entity).getHandle(), force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRiding() {
|
||||
handle.stopRiding();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getTickCount() {
|
||||
return handle.tickCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(boolean allStack) {
|
||||
handle.drop(allStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLastActionTime() {
|
||||
handle.resetLastActionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGround() {
|
||||
return handle.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpFromGround() {
|
||||
handle.jumpFromGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJumping(boolean jumping) {
|
||||
handle.setJumping(jumping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem() {
|
||||
return handle.isUsingItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableAdvancements(@NotNull Plugin plugin) {
|
||||
if (ServerPlayer$advancements == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
try {
|
||||
ServerPlayer$advancements.set(
|
||||
handle,
|
||||
new FakePlayerAdvancements(
|
||||
server.getFixerUpper(),
|
||||
server.getPlayerList(),
|
||||
server.getAdvancements(),
|
||||
plugin.getDataFolder().getParentFile().toPath(),
|
||||
handle
|
||||
)
|
||||
);
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int slot, boolean flag, boolean flag1) {
|
||||
var inventory = handle.getInventory();
|
||||
handle.drop(inventory.removeItem(slot, inventory.getItem(slot).getCount()), flag, flag1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayBefore() {
|
||||
player.readExtraData(new CompoundTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupClientOptions() {
|
||||
var option = new ClientInformation(
|
||||
"en_us",
|
||||
Bukkit.getViewDistance(),
|
||||
ChatVisiblity.SYSTEM,
|
||||
false,
|
||||
ConstantPool.MODEL_CUSTOMISATION,
|
||||
HumanoidArm.RIGHT,
|
||||
false,
|
||||
true,
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
handle.updateOptions(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn() {
|
||||
if (!this.player.isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
handle.connection.handleClientCommand(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapItemWithOffhand() {
|
||||
handle.connection.handlePlayerAction(new ServerboundPlayerActionPacket(
|
||||
ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND,
|
||||
new BlockPos(0, 0, 0),
|
||||
Direction.DOWN
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
fakeplayer-v1_21_4/pom.xml
Normal file
100
fakeplayer-v1_21_4/pom.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?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_4</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.4-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>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>${nms.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<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>
|
@ -0,0 +1,49 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.action;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
|
||||
public class AttackAction extends TraceAction {
|
||||
|
||||
private final ServerPlayer player;
|
||||
|
||||
public AttackAction(ServerPlayer player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.ENTITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
player.attack(entityHit.getEntity());
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
player.resetAttackStrengthTicker();
|
||||
player.resetLastActionTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*;
|
||||
|
||||
public class MineAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public MineAction(ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.BLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
|
||||
if (player.blockActionRestricted(player.level(), pos, player.gameMode.getGameModeForPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.pos != null && player.level().getBlockState(current.pos).isAir()) {
|
||||
current.pos = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var state = player.level().getBlockState(pos);
|
||||
var broken = false;
|
||||
if (player.gameMode.getGameModeForPlayer().isCreative()) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
} else if (current.pos == null || !current.pos.equals(pos)) {
|
||||
if (current.pos != null) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
|
||||
if (!state.isAir() && current.progress == 0) {
|
||||
state.attack(player.level(), pos, player);
|
||||
}
|
||||
|
||||
if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) {
|
||||
current.pos = null;
|
||||
broken = true;
|
||||
} else {
|
||||
current.pos = pos;
|
||||
current.progress = 0;
|
||||
}
|
||||
} else {
|
||||
current.progress += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (current.progress >= 1) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
STOP_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
}
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (current.progress * 10));
|
||||
}
|
||||
|
||||
player.resetLastActionTime();
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
return broken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public void stop() {
|
||||
if (current.pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.level().destroyBlockProgress(-1, current.pos, -1);
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
Direction.DOWN,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 0;
|
||||
current.progress = 0;
|
||||
}
|
||||
|
||||
private static class Current {
|
||||
|
||||
/**
|
||||
* 当前左键的目标位置
|
||||
*/
|
||||
@Nullable
|
||||
public BlockPos pos;
|
||||
|
||||
/**
|
||||
* 破坏方块的进度
|
||||
*/
|
||||
public float progress;
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.action;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.Action;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.action.util.Tracer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class TraceAction implements Action {
|
||||
|
||||
protected final ServerPlayer player;
|
||||
|
||||
protected TraceAction(@NotNull ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
protected @Nullable HitResult getTarget() {
|
||||
double reach = player.gameMode.isCreative() ? 5 : 4.5f;
|
||||
return Tracer.rayTrace(player, 1, reach, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.action;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class UseAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public UseAction(@NotNull ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isUsingItem()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var hand : InteractionHand.values()) {
|
||||
switch (hit.getType()) {
|
||||
case BLOCK -> {
|
||||
player.resetLastActionTime();
|
||||
var world = player.serverLevel();
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
if (pos.getY() < player.level().getMaxY() - (side == Direction.UP ? 1 : 0) && world.mayInteract(player, pos)) {
|
||||
var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit);
|
||||
if (result.consumesAction()) {
|
||||
player.swing(hand);
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
player.resetLastActionTime();
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
var entity = entityHit.getEntity();
|
||||
boolean handWasEmpty = player.getItemInHand(hand).isEmpty();
|
||||
boolean itemFrameEmpty = (entity instanceof ItemFrame) && ((ItemFrame) entity).getItem().isEmpty();
|
||||
var pos = entityHit.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
if (entity.interactAt(player, pos, hand).consumesAction()) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handItem = player.getItemInHand(hand);
|
||||
if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) {
|
||||
player.resetLastActionTime();
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
current.freeze = 0;
|
||||
player.releaseUsingItem();
|
||||
}
|
||||
|
||||
private final static class Current {
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.action.util;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* copy from fabric carpet mod
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
public static @Nullable HitResult rayTrace(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var blockHit = rayTraceBlocks(source, partialTicks, reach, fluids);
|
||||
double maxSqDist = reach * reach;
|
||||
if (blockHit != null) {
|
||||
maxSqDist = blockHit.getLocation().distanceToSqr(source.getEyePosition(partialTicks));
|
||||
}
|
||||
EntityHitResult entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist);
|
||||
return entityHit == null ? blockHit : entityHit;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static @Nullable BlockHitResult rayTraceBlocks(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var rotation = source.getViewVector(partialTicks);
|
||||
var reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach);
|
||||
return source.level().clip(new ClipContext(pos, reachEnd, ClipContext.Block.OUTLINE, fluids ?
|
||||
ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, source));
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
double maxSqDist
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var reachVec = source.getViewVector(partialTicks).scale(reach);
|
||||
var box = source.getBoundingBox().expandTowards(reachVec).inflate(1);
|
||||
return rayTraceEntities(source,
|
||||
pos,
|
||||
pos.add(reachVec),
|
||||
box,
|
||||
e -> !e.isSpectator() && e.isPickable(),
|
||||
maxSqDist);
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
@NotNull Vec3 start,
|
||||
@NotNull Vec3 end,
|
||||
@NotNull AABB box,
|
||||
@NotNull Predicate<Entity> predicate,
|
||||
double maxSqDistance
|
||||
) {
|
||||
@SuppressWarnings("resource")
|
||||
var world = source.level();
|
||||
double targetDistance = maxSqDistance;
|
||||
Entity target = null;
|
||||
Vec3 targetHitPos = null;
|
||||
for (Entity current : world.getEntities(source, box, predicate)) {
|
||||
var currentBox = current.getBoundingBox().inflate(current.getPickRadius());
|
||||
var currentHit = currentBox.clip(start, end);
|
||||
if (currentBox.contains(start)) {
|
||||
if (targetDistance >= 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHit.orElse(start);
|
||||
targetDistance = 0;
|
||||
}
|
||||
} else if (currentHit.isPresent()) {
|
||||
var currentHitPos = currentHit.get();
|
||||
var currentDistance = start.distanceToSqr(currentHitPos);
|
||||
if (currentDistance < targetDistance || targetDistance == 0) {
|
||||
if (current.getRootVehicle() == source.getRootVehicle()) {
|
||||
if (targetDistance == 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
targetDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target == null ? null : new EntityHitResult(target, targetHitPos);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import io.github.hello09x.fakeplayer.core.network.FakeChannel;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.PacketSendListener;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeConnection extends Connection {
|
||||
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
|
||||
public FakeConnection(@NotNull InetAddress address) {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
this.channel = new FakeChannel(null, address);
|
||||
this.address = this.channel.remoteAddress();
|
||||
Connection.configureSerialization(this.channel.pipeline(), PacketFlow.SERVERBOUND, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.network;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.ServerAdvancementManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FakePlayerAdvancements extends PlayerAdvancements {
|
||||
|
||||
public FakePlayerAdvancements(
|
||||
DataFixer datafixer,
|
||||
PlayerList playerlist,
|
||||
ServerAdvancementManager manager,
|
||||
Path path,
|
||||
ServerPlayer player
|
||||
) {
|
||||
super(datafixer, playerlist, manager, path, player);
|
||||
this.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean award(AdvancementHolder advancementholder, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushDirty(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
|
||||
return new AdvancementProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revoke(AdvancementHolder advancement, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedTab(AdvancementHolder advancement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopListening() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
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.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerImpl implements NMSServerGamePacketListener {
|
||||
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
|
||||
public FakeServerGamePacketListenerImpl(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull Connection connection,
|
||||
@NotNull ServerPlayer player,
|
||||
@NotNull CommonListenerCookie cookie
|
||||
) {
|
||||
super(server, connection, player, cookie);
|
||||
Optional.ofNullable(Bukkit.getPlayer(player.getUUID()))
|
||||
.map(CraftPlayer.class::cast)
|
||||
.ifPresent(p -> p.addChannel(BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCustomPayloadPacket(@NotNull ClientboundCustomPayloadPacket packet) {
|
||||
var payload = packet.payload();
|
||||
var resourceLocation = payload.type().id();
|
||||
var channel = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
|
||||
if (!channel.equals(BUNGEE_CORD_CORRECTED_CHANNEL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(payload instanceof DiscardedPayload p)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recipient = Bukkit
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(manager::isNotFake)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if (recipient == null) {
|
||||
log.warning("Failed to forward a plugin message cause non real players in the server");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = p.data().array();
|
||||
recipient.sendPluginMessage(Main.getInstance(), BUNGEE_CORD_CHANNEL, message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionSetting;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionTicker;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionType;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
|
||||
import io.github.hello09x.fakeplayer.core.entity.action.BaseActionTicker;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.action.AttackAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.action.MineAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.action.UseAction;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ActionTickerImpl extends BaseActionTicker implements ActionTicker {
|
||||
|
||||
public ActionTickerImpl(@NotNull NMSBridge nms, @NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
|
||||
super(nms, player, action, setting);
|
||||
if (this.action == null) {
|
||||
this.action = switch (action) {
|
||||
case ATTACK -> new AttackAction(((CraftPlayer) player).getHandle());
|
||||
case MINE -> new MineAction(((CraftPlayer) player).getHandle());
|
||||
case USE -> new UseAction(((CraftPlayer) player).getHandle());
|
||||
case JUMP, LOOK_AT_NEAREST_ENTITY, DROP_INVENTORY, DROP_STACK, DROP_ITEM ->
|
||||
throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.*;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
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.4");
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSEntity;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSEntityImpl implements NMSEntity {
|
||||
|
||||
@Getter
|
||||
private final Entity handle;
|
||||
|
||||
public NMSEntityImpl(@NotNull org.bukkit.entity.@NotNull Entity entity) {
|
||||
this.handle = ((CraftEntity) entity).getHandle();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.network.FakeConnection;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.network.FakeServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class NMSNetworkImpl implements NMSNetwork {
|
||||
|
||||
@NotNull
|
||||
private final FakeConnection connection;
|
||||
|
||||
private NMSServerGamePacketListener serverGamePacketListener;
|
||||
|
||||
public NMSNetworkImpl(
|
||||
@NotNull InetAddress address
|
||||
) {
|
||||
this.connection = new FakeConnection(address);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener placeNewPlayer(
|
||||
@NotNull Server server,
|
||||
@NotNull Player player
|
||||
) {
|
||||
var handle = ((CraftPlayer) player).getHandle();
|
||||
var cookie = CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile(), false);
|
||||
|
||||
((CraftServer) server).getHandle().placeNewPlayer(
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
|
||||
var listener = new FakeServerGamePacketListenerImpl(
|
||||
((CraftServer) server).getServer(),
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
this.serverGamePacketListener = listener;
|
||||
handle.connection = listener;
|
||||
return listener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener getServerGamePacketListener() throws IllegalStateException {
|
||||
if (this.serverGamePacketListener == null) {
|
||||
throw new IllegalStateException("not initialized");
|
||||
}
|
||||
return this.serverGamePacketListener;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.github.hello09x.devtools.core.utils.WorldUtils;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServer;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class NMSServerImpl implements NMSServer {
|
||||
|
||||
|
||||
@Getter
|
||||
private final MinecraftServer handle;
|
||||
|
||||
public NMSServerImpl(@NotNull Server server) {
|
||||
this.handle = ((CraftServer) server).getServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NMSServerPlayer newPlayer(@NotNull UUID uuid, @NotNull String name) {
|
||||
var handle = new ServerPlayer(
|
||||
new NMSServerImpl(Bukkit.getServer()).getHandle(),
|
||||
new NMSServerLevelImpl(WorldUtils.getMainWorld()).getHandle(),
|
||||
new GameProfile(uuid, name),
|
||||
ClientInformation.createDefault()
|
||||
);
|
||||
return new NMSServerPlayerImpl(handle.getBukkitEntity());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerLevel;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftWorld;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSServerLevelImpl implements NMSServerLevel {
|
||||
|
||||
@Getter
|
||||
private final ServerLevel handle;
|
||||
|
||||
public NMSServerLevelImpl(@NotNull World world) {
|
||||
this.handle = ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_4.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import io.github.hello09x.fakeplayer.core.constant.ConstantPool;
|
||||
import io.github.hello09x.fakeplayer.core.util.Reflections;
|
||||
import io.github.hello09x.fakeplayer.v1_21_4.network.FakePlayerAdvancements;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientCommandPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class NMSServerPlayerImpl implements NMSServerPlayer {
|
||||
|
||||
private final static Field ServerPlayer$advancements = Reflections.getFirstFieldByType(
|
||||
ServerPlayer.class,
|
||||
PlayerAdvancements.class,
|
||||
false
|
||||
);
|
||||
|
||||
@Getter
|
||||
private final ServerPlayer handle;
|
||||
|
||||
@Getter
|
||||
private final CraftPlayer player;
|
||||
|
||||
public NMSServerPlayerImpl(@NotNull Player player) {
|
||||
this.player = ((CraftPlayer) player);
|
||||
this.handle = ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return handle.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return handle.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return handle.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXo(double xo) {
|
||||
handle.xo = xo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYo(double yo) {
|
||||
handle.yo = yo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZo(double zo) {
|
||||
handle.zo = zo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
handle.doTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absMoveTo(double x, double y, double z, float yRot, float xRot) {
|
||||
handle.absMoveTo(x, y, z, yRot, xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYRot() {
|
||||
return handle.getYRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYRot(float yRot) {
|
||||
handle.setYRot(yRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXRot() {
|
||||
return handle.getXRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRot(float xRot) {
|
||||
handle.setXRot(xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZza() {
|
||||
return handle.zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZza(float zza) {
|
||||
handle.zza = zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXxa() {
|
||||
return handle.xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXxa(float xxa) {
|
||||
handle.xxa = xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeltaMovement(@NotNull Vector vector) {
|
||||
handle.setDeltaMovement(new Vec3(
|
||||
vector.getX(),
|
||||
vector.getY(),
|
||||
vector.getZ()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(@NotNull Entity entity, boolean force) {
|
||||
return handle.startRiding(new NMSEntityImpl(entity).getHandle(), force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRiding() {
|
||||
handle.stopRiding();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getTickCount() {
|
||||
return handle.tickCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(boolean allStack) {
|
||||
handle.drop(allStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLastActionTime() {
|
||||
handle.resetLastActionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGround() {
|
||||
return handle.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpFromGround() {
|
||||
handle.jumpFromGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJumping(boolean jumping) {
|
||||
handle.setJumping(jumping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem() {
|
||||
return handle.isUsingItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableAdvancements(@NotNull Plugin plugin) {
|
||||
if (ServerPlayer$advancements == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
try {
|
||||
ServerPlayer$advancements.set(
|
||||
handle,
|
||||
new FakePlayerAdvancements(
|
||||
server.getFixerUpper(),
|
||||
server.getPlayerList(),
|
||||
server.getAdvancements(),
|
||||
plugin.getDataFolder().getParentFile().toPath(),
|
||||
handle
|
||||
)
|
||||
);
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int slot, boolean flag, boolean flag1) {
|
||||
var inventory = handle.getInventory();
|
||||
handle.drop(inventory.removeItem(slot, inventory.getItem(slot).getCount()), flag, flag1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayBefore() {
|
||||
player.readExtraData(new CompoundTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupClientOptions() {
|
||||
var option = new ClientInformation(
|
||||
"en_us",
|
||||
Bukkit.getViewDistance(),
|
||||
ChatVisiblity.SYSTEM,
|
||||
false,
|
||||
ConstantPool.MODEL_CUSTOMISATION,
|
||||
HumanoidArm.RIGHT,
|
||||
false,
|
||||
true,
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
handle.updateOptions(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn() {
|
||||
if (!this.player.isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
handle.connection.handleClientCommand(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapItemWithOffhand() {
|
||||
handle.connection.handlePlayerAction(new ServerboundPlayerActionPacket(
|
||||
ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND,
|
||||
new BlockPos(0, 0, 0),
|
||||
Direction.DOWN
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
fakeplayer-v1_21_5/pom.xml
Normal file
100
fakeplayer-v1_21_5/pom.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?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_5</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.5-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>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>${nms.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<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>
|
@ -0,0 +1,49 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.action;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
|
||||
public class AttackAction extends TraceAction {
|
||||
|
||||
private final ServerPlayer player;
|
||||
|
||||
public AttackAction(ServerPlayer player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.ENTITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
player.attack(entityHit.getEntity());
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
player.resetAttackStrengthTicker();
|
||||
player.resetLastActionTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*;
|
||||
|
||||
public class MineAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public MineAction(ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.BLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
|
||||
if (player.blockActionRestricted(player.level(), pos, player.gameMode.getGameModeForPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.pos != null && player.level().getBlockState(current.pos).isAir()) {
|
||||
current.pos = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var state = player.level().getBlockState(pos);
|
||||
var broken = false;
|
||||
if (player.gameMode.getGameModeForPlayer().isCreative()) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
} else if (current.pos == null || !current.pos.equals(pos)) {
|
||||
if (current.pos != null) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
|
||||
if (!state.isAir() && current.progress == 0) {
|
||||
state.attack(player.level(), pos, player);
|
||||
}
|
||||
|
||||
if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) {
|
||||
current.pos = null;
|
||||
broken = true;
|
||||
} else {
|
||||
current.pos = pos;
|
||||
current.progress = 0;
|
||||
}
|
||||
} else {
|
||||
current.progress += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (current.progress >= 1) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
STOP_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
}
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (current.progress * 10));
|
||||
}
|
||||
|
||||
player.resetLastActionTime();
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
return broken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public void stop() {
|
||||
if (current.pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.level().destroyBlockProgress(-1, current.pos, -1);
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
Direction.DOWN,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 0;
|
||||
current.progress = 0;
|
||||
}
|
||||
|
||||
private static class Current {
|
||||
|
||||
/**
|
||||
* 当前左键的目标位置
|
||||
*/
|
||||
@Nullable
|
||||
public BlockPos pos;
|
||||
|
||||
/**
|
||||
* 破坏方块的进度
|
||||
*/
|
||||
public float progress;
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.action;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.Action;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.action.util.Tracer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class TraceAction implements Action {
|
||||
|
||||
protected final ServerPlayer player;
|
||||
|
||||
protected TraceAction(@NotNull ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
protected @Nullable HitResult getTarget() {
|
||||
double reach = player.gameMode.isCreative() ? 5 : 4.5f;
|
||||
return Tracer.rayTrace(player, 1, reach, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.action;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class UseAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public UseAction(@NotNull ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isUsingItem()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var hand : InteractionHand.values()) {
|
||||
switch (hit.getType()) {
|
||||
case BLOCK -> {
|
||||
player.resetLastActionTime();
|
||||
var world = player.serverLevel();
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
if (pos.getY() < player.level().getMaxY() - (side == Direction.UP ? 1 : 0) && world.mayInteract(player, pos)) {
|
||||
var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit);
|
||||
if (result.consumesAction()) {
|
||||
player.swing(hand);
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
player.resetLastActionTime();
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
var entity = entityHit.getEntity();
|
||||
boolean handWasEmpty = player.getItemInHand(hand).isEmpty();
|
||||
boolean itemFrameEmpty = (entity instanceof ItemFrame) && ((ItemFrame) entity).getItem().isEmpty();
|
||||
var pos = entityHit.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
if (entity.interactAt(player, pos, hand).consumesAction()) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handItem = player.getItemInHand(hand);
|
||||
if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) {
|
||||
player.resetLastActionTime();
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
current.freeze = 0;
|
||||
player.releaseUsingItem();
|
||||
}
|
||||
|
||||
private final static class Current {
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.action.util;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* copy from fabric carpet mod
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
public static @Nullable HitResult rayTrace(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var blockHit = rayTraceBlocks(source, partialTicks, reach, fluids);
|
||||
double maxSqDist = reach * reach;
|
||||
if (blockHit != null) {
|
||||
maxSqDist = blockHit.getLocation().distanceToSqr(source.getEyePosition(partialTicks));
|
||||
}
|
||||
EntityHitResult entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist);
|
||||
return entityHit == null ? blockHit : entityHit;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static @Nullable BlockHitResult rayTraceBlocks(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var rotation = source.getViewVector(partialTicks);
|
||||
var reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach);
|
||||
return source.level().clip(new ClipContext(pos, reachEnd, ClipContext.Block.OUTLINE, fluids ?
|
||||
ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, source));
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
double maxSqDist
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var reachVec = source.getViewVector(partialTicks).scale(reach);
|
||||
var box = source.getBoundingBox().expandTowards(reachVec).inflate(1);
|
||||
return rayTraceEntities(source,
|
||||
pos,
|
||||
pos.add(reachVec),
|
||||
box,
|
||||
e -> !e.isSpectator() && e.isPickable(),
|
||||
maxSqDist);
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
@NotNull Vec3 start,
|
||||
@NotNull Vec3 end,
|
||||
@NotNull AABB box,
|
||||
@NotNull Predicate<Entity> predicate,
|
||||
double maxSqDistance
|
||||
) {
|
||||
@SuppressWarnings("resource")
|
||||
var world = source.level();
|
||||
double targetDistance = maxSqDistance;
|
||||
Entity target = null;
|
||||
Vec3 targetHitPos = null;
|
||||
for (Entity current : world.getEntities(source, box, predicate)) {
|
||||
var currentBox = current.getBoundingBox().inflate(current.getPickRadius());
|
||||
var currentHit = currentBox.clip(start, end);
|
||||
if (currentBox.contains(start)) {
|
||||
if (targetDistance >= 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHit.orElse(start);
|
||||
targetDistance = 0;
|
||||
}
|
||||
} else if (currentHit.isPresent()) {
|
||||
var currentHitPos = currentHit.get();
|
||||
var currentDistance = start.distanceToSqr(currentHitPos);
|
||||
if (currentDistance < targetDistance || targetDistance == 0) {
|
||||
if (current.getRootVehicle() == source.getRootVehicle()) {
|
||||
if (targetDistance == 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
targetDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target == null ? null : new EntityHitResult(target, targetHitPos);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import io.github.hello09x.fakeplayer.core.network.FakeChannel;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.PacketSendListener;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeConnection extends Connection {
|
||||
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
|
||||
public FakeConnection(@NotNull InetAddress address) {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
this.channel = new FakeChannel(null, address);
|
||||
this.address = this.channel.remoteAddress();
|
||||
Connection.configureSerialization(this.channel.pipeline(), PacketFlow.SERVERBOUND, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.network;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.ServerAdvancementManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FakePlayerAdvancements extends PlayerAdvancements {
|
||||
|
||||
public FakePlayerAdvancements(
|
||||
DataFixer datafixer,
|
||||
PlayerList playerlist,
|
||||
ServerAdvancementManager manager,
|
||||
Path path,
|
||||
ServerPlayer player
|
||||
) {
|
||||
super(datafixer, playerlist, manager, path, player);
|
||||
this.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean award(AdvancementHolder advancementholder, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushDirty(ServerPlayer entityplayer, boolean flag) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
|
||||
return new AdvancementProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revoke(AdvancementHolder advancement, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedTab(AdvancementHolder advancement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopListening() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import lombok.Lombok;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
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.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerImpl implements NMSServerGamePacketListener {
|
||||
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
|
||||
public FakeServerGamePacketListenerImpl(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull Connection connection,
|
||||
@NotNull ServerPlayer player,
|
||||
@NotNull CommonListenerCookie cookie
|
||||
) {
|
||||
super(server, connection, player, cookie);
|
||||
Optional.ofNullable(Bukkit.getPlayer(player.getUUID()))
|
||||
.map(CraftPlayer.class::cast)
|
||||
.ifPresent(p -> p.addChannel(BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCustomPayloadPacket(@NotNull ClientboundCustomPayloadPacket packet) {
|
||||
var payload = packet.payload();
|
||||
var resourceLocation = payload.type().id();
|
||||
var channel = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
|
||||
if (!channel.equals(BUNGEE_CORD_CORRECTED_CHANNEL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(payload instanceof DiscardedPayload discardedPayload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recipient = Bukkit
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(manager::isNotFake)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if (recipient == null) {
|
||||
log.warning("Failed to forward a plugin message cause non real players in the server");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = getDiscardedPayloadData(discardedPayload);
|
||||
recipient.sendPluginMessage(Main.getInstance(), BUNGEE_CORD_CHANNEL, message);
|
||||
}
|
||||
|
||||
private byte[] getDiscardedPayloadData(@NotNull DiscardedPayload payload) {
|
||||
try {
|
||||
return payload.data().array();
|
||||
} catch (NoSuchMethodError e) {
|
||||
try {
|
||||
return (byte[]) payload.getClass().getMethod("data").invoke(payload); // 1.21.5 actual is `public final byte[] data() {}`
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionSetting;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionTicker;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionType;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
|
||||
import io.github.hello09x.fakeplayer.core.entity.action.BaseActionTicker;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.action.AttackAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.action.MineAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.action.UseAction;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ActionTickerImpl extends BaseActionTicker implements ActionTicker {
|
||||
|
||||
public ActionTickerImpl(@NotNull NMSBridge nms, @NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
|
||||
super(nms, player, action, setting);
|
||||
if (this.action == null) {
|
||||
this.action = switch (action) {
|
||||
case ATTACK -> new AttackAction(((CraftPlayer) player).getHandle());
|
||||
case MINE -> new MineAction(((CraftPlayer) player).getHandle());
|
||||
case USE -> new UseAction(((CraftPlayer) player).getHandle());
|
||||
case JUMP, LOOK_AT_NEAREST_ENTITY, DROP_INVENTORY, DROP_STACK, DROP_ITEM ->
|
||||
throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.*;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
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.5");
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSEntity;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSEntityImpl implements NMSEntity {
|
||||
|
||||
@Getter
|
||||
private final Entity handle;
|
||||
|
||||
public NMSEntityImpl(@NotNull org.bukkit.entity.@NotNull Entity entity) {
|
||||
this.handle = ((CraftEntity) entity).getHandle();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.network.FakeConnection;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.network.FakeServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class NMSNetworkImpl implements NMSNetwork {
|
||||
|
||||
@NotNull
|
||||
private final FakeConnection connection;
|
||||
|
||||
private NMSServerGamePacketListener serverGamePacketListener;
|
||||
|
||||
public NMSNetworkImpl(
|
||||
@NotNull InetAddress address
|
||||
) {
|
||||
this.connection = new FakeConnection(address);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener placeNewPlayer(
|
||||
@NotNull Server server,
|
||||
@NotNull Player player
|
||||
) {
|
||||
var handle = ((CraftPlayer) player).getHandle();
|
||||
var cookie = CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile(), false);
|
||||
|
||||
((CraftServer) server).getHandle().placeNewPlayer(
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
|
||||
var listener = new FakeServerGamePacketListenerImpl(
|
||||
((CraftServer) server).getServer(),
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
this.serverGamePacketListener = listener;
|
||||
handle.connection = listener;
|
||||
return listener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener getServerGamePacketListener() throws IllegalStateException {
|
||||
if (this.serverGamePacketListener == null) {
|
||||
throw new IllegalStateException("not initialized");
|
||||
}
|
||||
return this.serverGamePacketListener;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.github.hello09x.devtools.core.utils.WorldUtils;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServer;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class NMSServerImpl implements NMSServer {
|
||||
|
||||
|
||||
@Getter
|
||||
private final MinecraftServer handle;
|
||||
|
||||
public NMSServerImpl(@NotNull Server server) {
|
||||
this.handle = ((CraftServer) server).getServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NMSServerPlayer newPlayer(@NotNull UUID uuid, @NotNull String name) {
|
||||
var handle = new ServerPlayer(
|
||||
new NMSServerImpl(Bukkit.getServer()).getHandle(),
|
||||
new NMSServerLevelImpl(WorldUtils.getMainWorld()).getHandle(),
|
||||
new GameProfile(uuid, name),
|
||||
ClientInformation.createDefault()
|
||||
);
|
||||
return new NMSServerPlayerImpl(handle.getBukkitEntity());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerLevel;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSServerLevelImpl implements NMSServerLevel {
|
||||
|
||||
@Getter
|
||||
private final ServerLevel handle;
|
||||
|
||||
public NMSServerLevelImpl(@NotNull World world) {
|
||||
this.handle = ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_5.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import io.github.hello09x.fakeplayer.core.constant.ConstantPool;
|
||||
import io.github.hello09x.fakeplayer.core.util.Reflections;
|
||||
import io.github.hello09x.fakeplayer.v1_21_5.network.FakePlayerAdvancements;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientCommandPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class NMSServerPlayerImpl implements NMSServerPlayer {
|
||||
|
||||
private final static Field ServerPlayer$advancements = Reflections.getFirstFieldByType(
|
||||
ServerPlayer.class,
|
||||
PlayerAdvancements.class,
|
||||
false
|
||||
);
|
||||
|
||||
@Getter
|
||||
private final ServerPlayer handle;
|
||||
|
||||
@Getter
|
||||
private final CraftPlayer player;
|
||||
|
||||
public NMSServerPlayerImpl(@NotNull Player player) {
|
||||
this.player = ((CraftPlayer) player);
|
||||
this.handle = ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return handle.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return handle.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return handle.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXo(double xo) {
|
||||
handle.xo = xo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYo(double yo) {
|
||||
handle.yo = yo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZo(double zo) {
|
||||
handle.zo = zo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
handle.doTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absMoveTo(double x, double y, double z, float yRot, float xRot) {
|
||||
handle.absSnapTo(x, y, z, yRot, xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYRot() {
|
||||
return handle.getYRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYRot(float yRot) {
|
||||
handle.setYRot(yRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXRot() {
|
||||
return handle.getXRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRot(float xRot) {
|
||||
handle.setXRot(xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZza() {
|
||||
return handle.zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZza(float zza) {
|
||||
handle.zza = zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXxa() {
|
||||
return handle.xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXxa(float xxa) {
|
||||
handle.xxa = xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeltaMovement(@NotNull Vector vector) {
|
||||
handle.setDeltaMovement(new Vec3(
|
||||
vector.getX(),
|
||||
vector.getY(),
|
||||
vector.getZ()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(@NotNull Entity entity, boolean force) {
|
||||
return handle.startRiding(new NMSEntityImpl(entity).getHandle(), force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRiding() {
|
||||
handle.stopRiding();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getTickCount() {
|
||||
return handle.tickCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(boolean allStack) {
|
||||
handle.drop(allStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLastActionTime() {
|
||||
handle.resetLastActionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGround() {
|
||||
return handle.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpFromGround() {
|
||||
handle.jumpFromGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJumping(boolean jumping) {
|
||||
handle.setJumping(jumping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem() {
|
||||
return handle.isUsingItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableAdvancements(@NotNull Plugin plugin) {
|
||||
if (ServerPlayer$advancements == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
try {
|
||||
ServerPlayer$advancements.set(
|
||||
handle,
|
||||
new FakePlayerAdvancements(
|
||||
server.getFixerUpper(),
|
||||
server.getPlayerList(),
|
||||
server.getAdvancements(),
|
||||
plugin.getDataFolder().getParentFile().toPath(),
|
||||
handle
|
||||
)
|
||||
);
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int slot, boolean flag, boolean flag1) {
|
||||
var inventory = handle.getInventory();
|
||||
handle.drop(inventory.removeItem(slot, inventory.getItem(slot).getCount()), flag, flag1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayBefore() {
|
||||
player.readExtraData(new CompoundTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupClientOptions() {
|
||||
var option = new ClientInformation(
|
||||
"en_us",
|
||||
Bukkit.getViewDistance(),
|
||||
ChatVisiblity.SYSTEM,
|
||||
false,
|
||||
ConstantPool.MODEL_CUSTOMISATION,
|
||||
HumanoidArm.RIGHT,
|
||||
false,
|
||||
true,
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
handle.updateOptions(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn() {
|
||||
if (!this.player.isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
handle.connection.handleClientCommand(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapItemWithOffhand() {
|
||||
handle.connection.handlePlayerAction(new ServerboundPlayerActionPacket(
|
||||
ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND,
|
||||
new BlockPos(0, 0, 0),
|
||||
Direction.DOWN
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
fakeplayer-v1_21_6/pom.xml
Normal file
100
fakeplayer-v1_21_6/pom.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?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_6</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.6-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>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>${nms.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<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>
|
@ -0,0 +1,49 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.action;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
|
||||
public class AttackAction extends TraceAction {
|
||||
|
||||
private final ServerPlayer player;
|
||||
|
||||
public AttackAction(ServerPlayer player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.ENTITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
player.attack(entityHit.getEntity());
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
player.resetAttackStrengthTicker();
|
||||
player.resetLastActionTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action.*;
|
||||
|
||||
public class MineAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public MineAction(ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hit.getType() != HitResult.Type.BLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
|
||||
if (player.blockActionRestricted(player.level(), pos, player.gameMode.getGameModeForPlayer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.pos != null && player.level().getBlockState(current.pos).isAir()) {
|
||||
current.pos = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var state = player.level().getBlockState(pos);
|
||||
var broken = false;
|
||||
if (player.gameMode.getGameModeForPlayer().isCreative()) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
} else if (current.pos == null || !current.pos.equals(pos)) {
|
||||
if (current.pos != null) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
START_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
|
||||
if (!state.isAir() && current.progress == 0) {
|
||||
state.attack(player.level(), pos, player);
|
||||
}
|
||||
|
||||
if (!state.isAir() && state.getDestroyProgress(player, player.level(), pos) >= 1) {
|
||||
current.pos = null;
|
||||
broken = true;
|
||||
} else {
|
||||
current.pos = pos;
|
||||
current.progress = 0;
|
||||
}
|
||||
} else {
|
||||
current.progress += state.getDestroyProgress(player, player.level(), pos);
|
||||
if (current.progress >= 1) {
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
pos,
|
||||
STOP_DESTROY_BLOCK,
|
||||
side,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 5;
|
||||
broken = true;
|
||||
}
|
||||
player.level().destroyBlockProgress(-1, pos, (int) (current.progress * 10));
|
||||
}
|
||||
|
||||
player.resetLastActionTime();
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
return broken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public void stop() {
|
||||
if (current.pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.level().destroyBlockProgress(-1, current.pos, -1);
|
||||
player.gameMode.handleBlockBreakAction(
|
||||
current.pos,
|
||||
ABORT_DESTROY_BLOCK,
|
||||
Direction.DOWN,
|
||||
player.level().getMaxY(),
|
||||
-1
|
||||
);
|
||||
current.pos = null;
|
||||
current.freeze = 0;
|
||||
current.progress = 0;
|
||||
}
|
||||
|
||||
private static class Current {
|
||||
|
||||
/**
|
||||
* 当前左键的目标位置
|
||||
*/
|
||||
@Nullable
|
||||
public BlockPos pos;
|
||||
|
||||
/**
|
||||
* 破坏方块的进度
|
||||
*/
|
||||
public float progress;
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.action;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.Action;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.action.util.Tracer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class TraceAction implements Action {
|
||||
|
||||
protected final ServerPlayer player;
|
||||
|
||||
protected TraceAction(@NotNull ServerPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
protected @Nullable HitResult getTarget() {
|
||||
double reach = player.gameMode.isCreative() ? 5 : 4.5f;
|
||||
return Tracer.rayTrace(player, 1, reach, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.action;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class UseAction extends TraceAction {
|
||||
|
||||
private final Current current = new Current();
|
||||
|
||||
public UseAction(@NotNull ServerPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
public boolean tick() {
|
||||
if (current.freeze > 0) {
|
||||
current.freeze--;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isUsingItem()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hit = this.getTarget();
|
||||
if (hit == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var hand : InteractionHand.values()) {
|
||||
switch (hit.getType()) {
|
||||
case BLOCK -> {
|
||||
player.resetLastActionTime();
|
||||
var world = player.level();
|
||||
var blockHit = (BlockHitResult) hit;
|
||||
var pos = blockHit.getBlockPos();
|
||||
var side = blockHit.getDirection();
|
||||
if (pos.getY() < player.level().getMaxY() - (side == Direction.UP ? 1 : 0) && world.mayInteract(player, pos)) {
|
||||
var result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, blockHit);
|
||||
if (result.consumesAction()) {
|
||||
player.swing(hand);
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
player.resetLastActionTime();
|
||||
var entityHit = (EntityHitResult) hit;
|
||||
var entity = entityHit.getEntity();
|
||||
boolean handWasEmpty = player.getItemInHand(hand).isEmpty();
|
||||
boolean itemFrameEmpty = (entity instanceof ItemFrame) && ((ItemFrame) entity).getItem().isEmpty();
|
||||
var pos = entityHit.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
if (entity.interactAt(player, pos, hand).consumesAction()) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
if (player.interactOn(entity, hand).consumesAction() && !(handWasEmpty && itemFrameEmpty)) {
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handItem = player.getItemInHand(hand);
|
||||
if (player.gameMode.useItem(player, player.level(), handItem, hand).consumesAction()) {
|
||||
player.resetLastActionTime();
|
||||
current.freeze = 3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
current.freeze = 0;
|
||||
player.releaseUsingItem();
|
||||
}
|
||||
|
||||
private final static class Current {
|
||||
|
||||
/**
|
||||
* 冷却, 单位: tick
|
||||
*/
|
||||
public int freeze;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.action.util;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* copy from fabric carpet mod
|
||||
*/
|
||||
public class Tracer {
|
||||
|
||||
public static @Nullable HitResult rayTrace(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var blockHit = rayTraceBlocks(source, partialTicks, reach, fluids);
|
||||
double maxSqDist = reach * reach;
|
||||
if (blockHit != null) {
|
||||
maxSqDist = blockHit.getLocation().distanceToSqr(source.getEyePosition(partialTicks));
|
||||
}
|
||||
EntityHitResult entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist);
|
||||
return entityHit == null ? blockHit : entityHit;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static @Nullable BlockHitResult rayTraceBlocks(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
boolean fluids
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var rotation = source.getViewVector(partialTicks);
|
||||
var reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach);
|
||||
return source.level().clip(new ClipContext(pos, reachEnd, ClipContext.Block.OUTLINE, fluids ?
|
||||
ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, source));
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
float partialTicks,
|
||||
double reach,
|
||||
double maxSqDist
|
||||
) {
|
||||
var pos = source.getEyePosition(partialTicks);
|
||||
var reachVec = source.getViewVector(partialTicks).scale(reach);
|
||||
var box = source.getBoundingBox().expandTowards(reachVec).inflate(1);
|
||||
return rayTraceEntities(source,
|
||||
pos,
|
||||
pos.add(reachVec),
|
||||
box,
|
||||
e -> !e.isSpectator() && e.isPickable(),
|
||||
maxSqDist);
|
||||
}
|
||||
|
||||
public static @Nullable EntityHitResult rayTraceEntities(
|
||||
@NotNull Entity source,
|
||||
@NotNull Vec3 start,
|
||||
@NotNull Vec3 end,
|
||||
@NotNull AABB box,
|
||||
@NotNull Predicate<Entity> predicate,
|
||||
double maxSqDistance
|
||||
) {
|
||||
@SuppressWarnings("resource")
|
||||
var world = source.level();
|
||||
double targetDistance = maxSqDistance;
|
||||
Entity target = null;
|
||||
Vec3 targetHitPos = null;
|
||||
for (Entity current : world.getEntities(source, box, predicate)) {
|
||||
var currentBox = current.getBoundingBox().inflate(current.getPickRadius());
|
||||
var currentHit = currentBox.clip(start, end);
|
||||
if (currentBox.contains(start)) {
|
||||
if (targetDistance >= 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHit.orElse(start);
|
||||
targetDistance = 0;
|
||||
}
|
||||
} else if (currentHit.isPresent()) {
|
||||
var currentHitPos = currentHit.get();
|
||||
var currentDistance = start.distanceToSqr(currentHitPos);
|
||||
if (currentDistance < targetDistance || targetDistance == 0) {
|
||||
if (current.getRootVehicle() == source.getRootVehicle()) {
|
||||
if (targetDistance == 0) {
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = current;
|
||||
targetHitPos = currentHitPos;
|
||||
targetDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target == null ? null : new EntityHitResult(target, targetHitPos);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import io.github.hello09x.fakeplayer.core.network.FakeChannel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeConnection extends Connection {
|
||||
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
|
||||
public FakeConnection(@NotNull InetAddress address) {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
this.channel = new FakeChannel(null, address);
|
||||
this.address = this.channel.remoteAddress();
|
||||
Connection.configureSerialization(this.channel.pipeline(), PacketFlow.SERVERBOUND, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable ChannelFutureListener channelfuturelistener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable ChannelFutureListener channelfuturelistener, boolean flag) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.network;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.ServerAdvancementManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FakePlayerAdvancements extends PlayerAdvancements {
|
||||
|
||||
public FakePlayerAdvancements(
|
||||
DataFixer datafixer,
|
||||
PlayerList playerlist,
|
||||
ServerAdvancementManager manager,
|
||||
Path path,
|
||||
ServerPlayer player
|
||||
) {
|
||||
super(datafixer, playerlist, manager, path, player);
|
||||
this.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean award(AdvancementHolder advancementholder, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushDirty(ServerPlayer entityplayer, boolean flag) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
|
||||
return new AdvancementProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revoke(AdvancementHolder advancement, String s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(ServerPlayer player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedTab(AdvancementHolder advancement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopListening() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.network;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
import io.github.hello09x.fakeplayer.core.manager.FakeplayerManager;
|
||||
import lombok.Lombok;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
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.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FakeServerGamePacketListenerImpl extends ServerGamePacketListenerImpl implements NMSServerGamePacketListener {
|
||||
|
||||
private final FakeplayerManager manager = Main.getInjector().getInstance(FakeplayerManager.class);
|
||||
private final static Logger log = Main.getInstance().getLogger();
|
||||
|
||||
public FakeServerGamePacketListenerImpl(
|
||||
@NotNull MinecraftServer server,
|
||||
@NotNull Connection connection,
|
||||
@NotNull ServerPlayer player,
|
||||
@NotNull CommonListenerCookie cookie
|
||||
) {
|
||||
super(server, connection, player, cookie);
|
||||
Optional.ofNullable(Bukkit.getPlayer(player.getUUID()))
|
||||
.ifPresent(p -> this.addChannel(p, BUNGEE_CORD_CORRECTED_CHANNEL));
|
||||
}
|
||||
|
||||
private boolean addChannel(@NotNull Player player, @NotNull String channel) {
|
||||
try {
|
||||
var method = player.getClass().getMethod("addChannel", String.class);
|
||||
var ret = method.invoke(player, channel);
|
||||
if (ret instanceof Boolean success) {
|
||||
return success;
|
||||
}
|
||||
return true;
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet) {
|
||||
if (packet instanceof ClientboundCustomPayloadPacket p) {
|
||||
this.handleCustomPayloadPacket(p);
|
||||
} else if (packet instanceof ClientboundSetEntityMotionPacket p) {
|
||||
this.handleClientboundSetEntityMotionPacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 玩家被击退的动作由客户端完成, 假人没有客户端因此手动完成这个动作
|
||||
*/
|
||||
public void handleClientboundSetEntityMotionPacket(@NotNull ClientboundSetEntityMotionPacket packet) {
|
||||
if (packet.getId() == this.player.getId() && this.player.hurtMarked) {
|
||||
Bukkit.getScheduler().runTask(Main.getInstance(), () -> {
|
||||
this.player.hurtMarked = true;
|
||||
this.player.lerpMotion(packet.getXa(), packet.getYa(), packet.getZa());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCustomPayloadPacket(@NotNull ClientboundCustomPayloadPacket packet) {
|
||||
var payload = packet.payload();
|
||||
var resourceLocation = payload.type().id();
|
||||
var channel = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
||||
|
||||
if (!channel.equals(BUNGEE_CORD_CORRECTED_CHANNEL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(payload instanceof DiscardedPayload discardedPayload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recipient = Bukkit
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(manager::isNotFake)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if (recipient == null) {
|
||||
log.warning("Failed to forward a plugin message cause non real players in the server");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = getDiscardedPayloadData(discardedPayload);
|
||||
recipient.sendPluginMessage(Main.getInstance(), BUNGEE_CORD_CHANNEL, message);
|
||||
}
|
||||
|
||||
private byte[] getDiscardedPayloadData(@NotNull DiscardedPayload payload) {
|
||||
try {
|
||||
return payload.data().array();
|
||||
} catch (NoSuchMethodError e) {
|
||||
try {
|
||||
return (byte[]) payload.getClass().getMethod("data").invoke(payload); // 1.21.5 actual is `public final byte[] data() {}`
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionSetting;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionTicker;
|
||||
import io.github.hello09x.fakeplayer.api.spi.ActionType;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSBridge;
|
||||
import io.github.hello09x.fakeplayer.core.entity.action.BaseActionTicker;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.action.AttackAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.action.MineAction;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.action.UseAction;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ActionTickerImpl extends BaseActionTicker implements ActionTicker {
|
||||
|
||||
public ActionTickerImpl(@NotNull NMSBridge nms, @NotNull Player player, @NotNull ActionType action, @NotNull ActionSetting setting) {
|
||||
super(nms, player, action, setting);
|
||||
if (this.action == null) {
|
||||
this.action = switch (action) {
|
||||
case ATTACK -> new AttackAction(((CraftPlayer) player).getHandle());
|
||||
case MINE -> new MineAction(((CraftPlayer) player).getHandle());
|
||||
case USE -> new UseAction(((CraftPlayer) player).getHandle());
|
||||
case JUMP, LOOK_AT_NEAREST_ENTITY, DROP_INVENTORY, DROP_STACK, DROP_ITEM ->
|
||||
throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.*;
|
||||
import io.github.hello09x.fakeplayer.core.Main;
|
||||
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.6");
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSEntity;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSEntityImpl implements NMSEntity {
|
||||
|
||||
@Getter
|
||||
private final Entity handle;
|
||||
|
||||
public NMSEntityImpl(@NotNull org.bukkit.entity.@NotNull Entity entity) {
|
||||
this.handle = ((CraftEntity) entity).getHandle();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSNetwork;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerGamePacketListener;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.network.FakeConnection;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.network.FakeServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class NMSNetworkImpl implements NMSNetwork {
|
||||
|
||||
@NotNull
|
||||
private final FakeConnection connection;
|
||||
|
||||
private NMSServerGamePacketListener serverGamePacketListener;
|
||||
|
||||
public NMSNetworkImpl(
|
||||
@NotNull InetAddress address
|
||||
) {
|
||||
this.connection = new FakeConnection(address);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener placeNewPlayer(
|
||||
@NotNull Server server,
|
||||
@NotNull Player player
|
||||
) {
|
||||
var handle = ((CraftPlayer) player).getHandle();
|
||||
var cookie = CommonListenerCookie.createInitial(((CraftPlayer) player).getProfile(), false);
|
||||
|
||||
((CraftServer) server).getHandle().placeNewPlayer(
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
|
||||
var listener = new FakeServerGamePacketListenerImpl(
|
||||
((CraftServer) server).getServer(),
|
||||
this.connection,
|
||||
handle,
|
||||
cookie
|
||||
);
|
||||
this.serverGamePacketListener = listener;
|
||||
handle.connection = listener;
|
||||
return listener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NMSServerGamePacketListener getServerGamePacketListener() throws IllegalStateException {
|
||||
if (this.serverGamePacketListener == null) {
|
||||
throw new IllegalStateException("not initialized");
|
||||
}
|
||||
return this.serverGamePacketListener;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.github.hello09x.devtools.core.utils.WorldUtils;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServer;
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class NMSServerImpl implements NMSServer {
|
||||
|
||||
|
||||
@Getter
|
||||
private final MinecraftServer handle;
|
||||
|
||||
public NMSServerImpl(@NotNull Server server) {
|
||||
this.handle = ((CraftServer) server).getServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NMSServerPlayer newPlayer(@NotNull UUID uuid, @NotNull String name) {
|
||||
var handle = new ServerPlayer(
|
||||
new NMSServerImpl(Bukkit.getServer()).getHandle(),
|
||||
new NMSServerLevelImpl(WorldUtils.getMainWorld()).getHandle(),
|
||||
new GameProfile(uuid, name),
|
||||
ClientInformation.createDefault()
|
||||
);
|
||||
return new NMSServerPlayerImpl(handle.getBukkitEntity());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerLevel;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NMSServerLevelImpl implements NMSServerLevel {
|
||||
|
||||
@Getter
|
||||
private final ServerLevel handle;
|
||||
|
||||
public NMSServerLevelImpl(@NotNull World world) {
|
||||
this.handle = ((CraftWorld) world).getHandle();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_6.spi;
|
||||
|
||||
import io.github.hello09x.fakeplayer.api.spi.NMSServerPlayer;
|
||||
import io.github.hello09x.fakeplayer.core.constant.ConstantPool;
|
||||
import io.github.hello09x.fakeplayer.core.util.Reflections;
|
||||
import io.github.hello09x.fakeplayer.v1_21_6.network.FakePlayerAdvancements;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientCommandPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ParticleStatus;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
import net.minecraft.world.level.storage.ValueInputContextHelper;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class NMSServerPlayerImpl implements NMSServerPlayer {
|
||||
|
||||
private final static Field ServerPlayer$advancements = Reflections.getFirstFieldByType(
|
||||
ServerPlayer.class,
|
||||
PlayerAdvancements.class,
|
||||
false
|
||||
);
|
||||
|
||||
@Getter
|
||||
private final ServerPlayer handle;
|
||||
|
||||
@Getter
|
||||
private final CraftPlayer player;
|
||||
|
||||
public NMSServerPlayerImpl(@NotNull Player player) {
|
||||
this.player = ((CraftPlayer) player);
|
||||
this.handle = ((CraftPlayer) player).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return handle.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return handle.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return handle.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXo(double xo) {
|
||||
handle.xo = xo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYo(double yo) {
|
||||
handle.yo = yo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZo(double zo) {
|
||||
handle.zo = zo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTick() {
|
||||
handle.doTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absMoveTo(double x, double y, double z, float yRot, float xRot) {
|
||||
handle.absSnapTo(x, y, z, yRot, xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYRot() {
|
||||
return handle.getYRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setYRot(float yRot) {
|
||||
handle.setYRot(yRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXRot() {
|
||||
return handle.getXRot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRot(float xRot) {
|
||||
handle.setXRot(xRot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZza() {
|
||||
return handle.zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZza(float zza) {
|
||||
handle.zza = zza;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXxa() {
|
||||
return handle.xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXxa(float xxa) {
|
||||
handle.xxa = xxa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeltaMovement(@NotNull Vector vector) {
|
||||
handle.setDeltaMovement(new Vec3(
|
||||
vector.getX(),
|
||||
vector.getY(),
|
||||
vector.getZ()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(@NotNull Entity entity, boolean force) {
|
||||
return handle.startRiding(new NMSEntityImpl(entity).getHandle(), force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRiding() {
|
||||
handle.stopRiding();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getTickCount() {
|
||||
return handle.tickCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(boolean allStack) {
|
||||
handle.drop(allStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLastActionTime() {
|
||||
handle.resetLastActionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGround() {
|
||||
return handle.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpFromGround() {
|
||||
handle.jumpFromGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJumping(boolean jumping) {
|
||||
handle.setJumping(jumping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingItem() {
|
||||
return handle.isUsingItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableAdvancements(@NotNull Plugin plugin) {
|
||||
if (ServerPlayer$advancements == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var server = ((CraftServer) Bukkit.getServer()).getServer();
|
||||
try {
|
||||
ServerPlayer$advancements.set(
|
||||
handle,
|
||||
new FakePlayerAdvancements(
|
||||
server.getFixerUpper(),
|
||||
server.getPlayerList(),
|
||||
server.getAdvancements(),
|
||||
plugin.getDataFolder().getParentFile().toPath(),
|
||||
handle
|
||||
)
|
||||
);
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int slot, boolean flag, boolean flag1) {
|
||||
var inventory = handle.getInventory();
|
||||
handle.drop(inventory.removeItem(slot, inventory.getItem(slot).getCount()), flag, flag1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayBefore() {
|
||||
player.readExtraData(new ValueInputContextHelper(HolderLookup.Provider.create(Stream.empty()), null).empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupClientOptions() {
|
||||
var option = new ClientInformation(
|
||||
"en_us",
|
||||
Bukkit.getViewDistance(),
|
||||
ChatVisiblity.SYSTEM,
|
||||
false,
|
||||
ConstantPool.MODEL_CUSTOMISATION,
|
||||
HumanoidArm.RIGHT,
|
||||
false,
|
||||
true,
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
handle.updateOptions(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn() {
|
||||
if (!this.player.isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
handle.connection.handleClientCommand(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapItemWithOffhand() {
|
||||
handle.connection.handlePlayerAction(new ServerboundPlayerActionPacket(
|
||||
ServerboundPlayerActionPacket.Action.SWAP_ITEM_WITH_OFFHAND,
|
||||
new BlockPos(0, 0, 0),
|
||||
Direction.DOWN
|
||||
));
|
||||
}
|
||||
|
||||
}
|
106
fakeplayer-v1_21_7/pom.xml
Normal file
106
fakeplayer-v1_21_7/pom.xml
Normal 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_7</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.7-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>
|
@ -0,0 +1,55 @@
|
||||
package io.github.hello09x.fakeplayer.v1_21_7.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.7");
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
106
fakeplayer-v1_21_8/pom.xml
Normal file
106
fakeplayer-v1_21_8/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
16
pom.xml
16
pom.xml
@ -18,18 +18,24 @@
|
||||
<module>fakeplayer-v1_20_2</module>
|
||||
<module>fakeplayer-v1_20_3</module>
|
||||
<module>fakeplayer-v1_20_4</module>
|
||||
<module>fakeplayer-v1_20_5</module>
|
||||
<module>fakeplayer-v1_20_6</module>
|
||||
<module>fakeplayer-v1_21</module>
|
||||
<module>fakeplayer-v1_21_1</module>
|
||||
<module>fakeplayer-v1_20_5</module>
|
||||
<module>fakeplayer-v1_21_3</module>
|
||||
<module>fakeplayer-v1_21_4</module>
|
||||
<module>fakeplayer-v1_21_5</module>
|
||||
<module>fakeplayer-v1_21_6</module>
|
||||
<module>fakeplayer-v1_21_7</module>
|
||||
<module>fakeplayer-v1_21_8</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>0.3.10</revision>
|
||||
<revision>0.3.18</revision>
|
||||
|
||||
<detools.version>0.1.4-SNAPSHOT</detools.version>
|
||||
<detools.version>0.1.6-SNAPSHOT</detools.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@ -60,7 +66,7 @@
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.20-R0.1-SNAPSHOT</version>
|
||||
<version>1.21.7-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@ -99,7 +105,7 @@
|
||||
<dependency>
|
||||
<groupId>dev.jorel</groupId>
|
||||
<artifactId>commandapi-bukkit-core</artifactId>
|
||||
<version>9.2.0</version>
|
||||
<version>10.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user