Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da005b117a | |||
| 41e24e8f91 | |||
| feec643a0e | |||
| 1861578557 | |||
| 3f3618850b | |||
| 46771456de | |||
| f67b68e4b7 | |||
| 4bb5b6c3da | |||
| 40aaa5f824 |
+1
-1
@@ -11,4 +11,4 @@ issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
custom: https://afdian.com/a/Luminol
|
||||
custom: [https://afdian.com/a/bm0721, https://afdian.com/a/Luminol] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Configure Git User Details
|
||||
run: git config --global user.email "ci@luminolmc.com" && git config --global user.name "LuminolMC CI"
|
||||
run: git config --global user.email "ci@lophinelabs.com" && git config --global user.name "LophineLabsCI"
|
||||
|
||||
- name: Apply All Patches
|
||||
run: ./gradlew --refresh-dependencies applyAllPatches
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
tag: ${{ env.tag }}
|
||||
name: ${{ env.project_id_b }} ${{ env.mcversion }} - ${{ env.commit_id }}${{ env.release_count }}
|
||||
body: |
|
||||
📦Version: `${{ env.mcversion }}` | Commit ${{ env.commit_id }} [](https://github.com/LuminolMC/${{ env.project_id }}/releases/download/${{ env.tag }}/${{ env.jar }})
|
||||
📦Version: `${{ env.mcversion }}` | Commit ${{ env.commit_id }} [](https://github.com/LophineLabs/${{ env.project_id }}/releases/download/${{ env.tag }}/${{ env.jar }})
|
||||
This release is automatically compiled by GitHub Actions
|
||||
### Branch Info
|
||||
> ${{ github.ref_name }}
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
tag: ${{ env.tag }}
|
||||
name: ${{ env.project_id_b }} ${{ env.mcversion }} - ${{ env.commit_id }}${{ env.release_count }}
|
||||
body: |
|
||||
📦Version: `${{ env.mcversion }}` | Commit ${{ env.commit_id }} [](https://github.com/LuminolMC/${{ env.project_id }}/releases/download/${{ env.tag }}/${{ env.jar }})
|
||||
📦Version: `${{ env.mcversion }}` | Commit ${{ env.commit_id }} [](https://github.com/LophineLabs/${{ env.project_id }}/releases/download/${{ env.tag }}/${{ env.jar }})
|
||||
This release is automatically compiled by GitHub Actions
|
||||
### Comments
|
||||
> ${{ inputs.comments }}
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
|
||||
*Lophine 是一个基于Luminol的分支,具有许多有用的优化和可配置的原版特性,目标是在Folia上实现更多生电的内容(请注意,完整生电请使用Fabric)*
|
||||
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LuminolMC/Lophine/issues)
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LophineLabs/Lophine/issues)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
[English](./README_EN.md) | **中文**
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
## 📥 下载
|
||||
|
||||
### 稳定版本
|
||||
所有发布版本都可以在 [Releases](https://github.com/LuminolMC/Lophine/releases) 页面找到。
|
||||
所有发布版本都可以在 [Releases](https://github.com/LophineLabs/Lophine/releases) 页面找到。
|
||||
|
||||
### 开发版本
|
||||
如果您想体验最新功能,可以通过以下步骤自行构建。
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://github.com/LuminolMC/Lophine.git
|
||||
git clone https://github.com/LophineLabs/Lophine.git
|
||||
cd Lophine
|
||||
|
||||
# 应用补丁并构建 Paperclip JAR
|
||||
@@ -91,15 +91,13 @@ dependencies {
|
||||
|
||||
### 加入我们的社区
|
||||
|
||||
- **QQ群**: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=QML5kIVsniPi1PlZvnjHQT_02EHsZ5Jc&authKey=%2FTCJsZC7JFQ9sxAroPCKuYnlV57Z5fyqp36ewXZk3Sn4iJ9p4MB1JKdc%2FFcX3HOM&noverify=0&group_code=1015048616)
|
||||
- **QQ频道**: [点击加入](https://pd.qq.com/s/eq9krf9j)
|
||||
- **Telegram**: [点击加入](https://t.me/LuminolMinecraft)
|
||||
- **Discord**: [点击加入](https://discord.gg/Qd7m3V6eDx)
|
||||
- **QQ群**: [1020403749](https://qm.qq.com/cgi-bin/qm/qr?k=y_MA9UaN7PM9e9J1LIs9Eea3LK8C0h6J&jump_from=webapi&authKey=ap5f8MlbeezXYtnmpnT5ZOFljDuOyV6OAb2PIcViQ+Ilr60Ycq63FDDTsJOZDYtj)
|
||||
- **Discord**: [点击加入](https://discord.gg/UXSgPZczcy)
|
||||
|
||||
### 获取帮助
|
||||
|
||||
- 📋 [提交 Issue](https://github.com/LuminolMC/Lophine/issues)
|
||||
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
|
||||
- 📋 [提交 Issue](https://github.com/LophineLabs/Lophine/issues)
|
||||
- 💬 [GitHub Discussions](https://github.com/LophineLabs/Lophine/discussions)
|
||||
- 📖 [项目文档](./docs/)
|
||||
|
||||
## 🐛 问题反馈
|
||||
@@ -124,34 +122,6 @@ dependencies {
|
||||
|
||||

|
||||
|
||||
## 🎉 特别感谢
|
||||
|
||||
### 项目赞助商
|
||||
|
||||
<div align="center">
|
||||
<b>感谢 <a href="https://github.com/LegacyLands">LegacyLands</a> 对本项目的赞助</b>
|
||||
<br>
|
||||
<i>如果你想开发跨 Folia/非 Folia 平台的插件,<a href="https://github.com/LegacyLands/legacy-lands-library/">legacy-lands-library</a> 将会是个不错的选择</i>
|
||||
<br><br>
|
||||
<img src="public/image/legacy-lands-logo.png" alt="LegacyLands Logo" width="200">
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⭐ 请给我们一个 Star!
|
||||
|
||||
> 你的每一个免费的 ⭐Star 就是我们每一个前进的动力。
|
||||
|
||||
### Star 历史
|
||||
|
||||
<a href="https://star-history.com/#LuminolMC/Luminol&LuminolMC/LightingLuminol&LuminolMC/Lophine&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
|
||||
<img alt="Star历史表" src="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<div align="center">
|
||||
<b>如果这个项目对您有帮助,请不要忘记给我们一个 ⭐Star!</b>
|
||||
</div>
|
||||
+13
-43
@@ -5,15 +5,15 @@
|
||||
|
||||
*Lophine is a Luminol fork with many useful optimizations and configurable vanilla features, aims to provide more function for survival-usable circuit on folia (Please note that Fabric should be used for complete survival-usable)*
|
||||
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LuminolMC/Lophine/issues)
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LophineLabs/Lophine/issues)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
**English** | [中文](./README.md)
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
## 📥 Download
|
||||
|
||||
### Stable Releases
|
||||
All release versions can be found on the [Releases](https://github.com/LuminolMC/Lophine/releases) page.
|
||||
All release versions can be found on the [Releases](https://github.com/LophineLabs/Lophine/releases) page.
|
||||
|
||||
### Development Builds
|
||||
If you want to experience the latest features, you can build it yourself following the steps below.
|
||||
@@ -41,7 +41,7 @@ If you want to experience the latest features, you can build it yourself followi
|
||||
|
||||
```bash
|
||||
# Clone the project
|
||||
git clone https://github.com/LuminolMC/Lophine.git
|
||||
git clone https://github.com/LophineLabs/Lophine.git
|
||||
cd Lophine
|
||||
|
||||
# Apply patches and build Paperclip JAR
|
||||
@@ -92,15 +92,13 @@ dependencies {
|
||||
|
||||
### Join Our Community
|
||||
|
||||
- **QQ Group**: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=QML5kIVsniPi1PlZvnjHQT_02EHsZ5Jc&authKey=%2FTCJsZC7JFQ9sxAroPCKuYnlV57Z5fyqp36ewXZk3Sn4iJ9p4MB1JKdc%2FFcX3HOM&noverify=0&group_code=1015048616)
|
||||
- **QQ Channel**: [Join Here](https://pd.qq.com/s/eq9krf9j)
|
||||
- **Telegram**: [Join Here](https://t.me/LuminolMinecraft)
|
||||
- **Discord**: [Join Here](https://discord.gg/Qd7m3V6eDx)
|
||||
- **QQ Group**: [1020403749](https://qm.qq.com/cgi-bin/qm/qr?k=y_MA9UaN7PM9e9J1LIs9Eea3LK8C0h6J&jump_from=webapi&authKey=ap5f8MlbeezXYtnmpnT5ZOFljDuOyV6OAb2PIcViQ+Ilr60Ycq63FDDTsJOZDYtj)
|
||||
- **Discord**: [Join Here](https://discord.gg/UXSgPZczcy)
|
||||
|
||||
### Get Help
|
||||
|
||||
- 📋 [Submit Issues](https://github.com/LuminolMC/Lophine/issues)
|
||||
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
|
||||
- 📋 [Submit Issues](https://github.com/LophineLabs/Lophine/issues)
|
||||
- 💬 [GitHub Discussions](https://github.com/LophineLabs/Lophine/discussions)
|
||||
- 📖 [Project Documentation](./docs/)
|
||||
|
||||
## 🐛 Bug Reports
|
||||
@@ -125,34 +123,6 @@ We welcome community contributions! For detailed contribution guidelines, please
|
||||
|
||||

|
||||
|
||||
## 🎉 Special Thanks
|
||||
|
||||
### Project Sponsors
|
||||
|
||||
<div align="center">
|
||||
<b>Thanks to <a href="https://github.com/LegacyLands">LegacyLands</a> for sponsoring this project</b>
|
||||
<br>
|
||||
<i>If you want to develop cross-Folia/non-Folia platform plugins, <a href="https://github.com/LegacyLands/legacy-lands-library/">legacy-lands-library</a> would be a great choice</i>
|
||||
<br><br>
|
||||
<img src="public/image/legacy-lands-logo.png" alt="LegacyLands Logo" width="200">
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⭐ Give Us a Star!
|
||||
|
||||
> Every free ⭐Star you give is the motivation for our every step forward.
|
||||
|
||||
### Star History
|
||||
|
||||
<a href="https://star-history.com/#LuminolMC/Luminol&LuminolMC/LightingLuminol&LuminolMC/Lophine&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<div align="center">
|
||||
<b>If this project helps you, please don't forget to give us a ⭐Star!</b>
|
||||
</div>
|
||||
+1
-1
@@ -9,7 +9,7 @@ plugins {
|
||||
paperweight {
|
||||
filterPatches = false
|
||||
upstreams.register("luminol") {
|
||||
repo = github("LuminolMC", "Luminol")
|
||||
repo = github("LuminolCustomArchive", "Luminol")
|
||||
ref = providers.gradleProperty("luminolRef")
|
||||
|
||||
println("Upstream commit ref: " + ref.get())
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.leavesmc.leaves.event.bot;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.entity.bot.Bot;
|
||||
|
||||
public class BotActionGuiOpenEvent extends BotEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private final Player player;
|
||||
private boolean cancel = false;
|
||||
|
||||
public BotActionGuiOpenEvent(@NotNull Bot who, @Nullable Player player) {
|
||||
super(who);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Player getOpenPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
}
|
||||
val log4jPlugins = sourceSets.create("log4jPlugins") {
|
||||
@@ -158,7 +_,7 @@
|
||||
@@ -158,10 +_,10 @@
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -50,7 +50,11 @@
|
||||
+ implementation(project(":lophine-api"))
|
||||
// Luminol start - Dependenices insert
|
||||
implementation("net.objecthunter:exp4j:0.4.8")
|
||||
implementation("com.electronwill.night-config:toml:3.9.0") // Night config
|
||||
- implementation("com.electronwill.night-config:toml:3.9.0") // Night config
|
||||
+ implementation("com.electronwill.night-config:toml:3.8.4") // Night config
|
||||
implementation("com.github.luben:zstd-jni:1.5.4-1")
|
||||
implementation("net.openhft:zero-allocation-hashing:0.16")
|
||||
implementation("io.github.classgraph:classgraph:4.8.158")
|
||||
@@ -234,14 +_,14 @@
|
||||
val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim()
|
||||
attributes(
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrHua269 <mrhua269@gmail.com>
|
||||
Date: Wed, 8 Jul 2026 21:37:19 +0800
|
||||
Subject: [PATCH] Camellia: Fix teleport missing original rotation and velocity
|
||||
|
||||
|
||||
diff --git a/io/papermc/paper/threadedregions/TeleportUtils.java b/io/papermc/paper/threadedregions/TeleportUtils.java
|
||||
index 2a64a5b2cf049661fe3f5a22ddfa39979624f5ec..335e9775e0d9a61f0e9641aba691a3bdf3ac7f2b 100644
|
||||
--- a/io/papermc/paper/threadedregions/TeleportUtils.java
|
||||
+++ b/io/papermc/paper/threadedregions/TeleportUtils.java
|
||||
@@ -41,7 +41,7 @@ public final class TeleportUtils {
|
||||
return;
|
||||
}
|
||||
(useFromRootVehicle ? realFrom.getRootVehicle() : realFrom).teleportAsync(
|
||||
- ((CraftWorld)loc.getWorld()).getHandle(), pos, null, null, null,
|
||||
+ ((CraftWorld)loc.getWorld()).getHandle(), pos, yaw, pitch, null, // Camellia - Fix teleport missing original rotation and velocity
|
||||
cause, teleportFlags, onComplete
|
||||
);
|
||||
},
|
||||
diff --git a/net/minecraft/server/commands/TeleportCommand.java b/net/minecraft/server/commands/TeleportCommand.java
|
||||
index f3b697eda0f47a2d1c64d7978ccdfb77fa226929..f657cda7c3856623f60f00f9fa44367ea771133c 100644
|
||||
--- a/net/minecraft/server/commands/TeleportCommand.java
|
||||
+++ b/net/minecraft/server/commands/TeleportCommand.java
|
||||
@@ -247,10 +247,8 @@ public class TeleportCommand {
|
||||
// Folia start - region threading
|
||||
if (true) {
|
||||
Vec3 posFinal = new Vec3(x, y, z);
|
||||
- //Float yawFinal = Float.valueOf(newYRot); // Luminol - fix teleport yaw issue
|
||||
- //Float pitchFinal = Float.valueOf(newXRot); // Luminol - fix teleport yaw issue
|
||||
- Float yawFinal = Float.valueOf(newYRot + victim.getYRot()); // Luminol - fix teleport yaw issue
|
||||
- Float pitchFinal = Float.valueOf(newXRot + victim.getXRot()); // Luminol - fix teleport pitch issue
|
||||
+ Float yawFinal = Float.valueOf(newYRot + victim.getYRot()); // Camellia - Fix teleport missing original rotation and velocity
|
||||
+ Float pitchFinal = Float.valueOf(newXRot + victim.getXRot()); // Camellia - Fix teleport missing original rotation and velocity
|
||||
victim.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> {
|
||||
nmsEntity.stopRiding();
|
||||
nmsEntity.teleportAsync(
|
||||
diff --git a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
|
||||
index bc9ca4680c18f28b97c4d1afe202cc2fff320daf..08c3b8ee0ed015572823bb0218ac7522ec8945b3 100644
|
||||
--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
|
||||
+++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java
|
||||
@@ -156,7 +156,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
||||
}
|
||||
|
||||
entity.teleportAsync(
|
||||
- checkWorld, to, null, null, null,
|
||||
+ checkWorld, to, null, null, entity.getDeltaMovement(), // Camellia - Fix teleport missing original rotation and velocity
|
||||
org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL,
|
||||
// chunk could have been unloaded
|
||||
Entity.TELEPORT_FLAG_TELEPORT_PASSENGERS | Entity.TELEPORT_FLAG_LOAD_CHUNK,
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NanaChiyo0721 <nanachiyo0721@163.com>
|
||||
Date: Mon, 13 Jul 2026 23:10:31 +0800
|
||||
Subject: [PATCH] Camellia: Replace packet processing queue with mt-queue
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/PacketProcessor.java b/net/minecraft/network/PacketProcessor.java
|
||||
index a25bc31c04778bf12e3b6301ffcfb2275fcc2d90..b91c669438dc4692144143c8eef0d3450a824969 100644
|
||||
--- a/net/minecraft/network/PacketProcessor.java
|
||||
+++ b/net/minecraft/network/PacketProcessor.java
|
||||
@@ -11,7 +11,7 @@ import org.slf4j.Logger;
|
||||
|
||||
public class PacketProcessor implements AutoCloseable {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- private final Queue<PacketProcessor.ListenerAndPacket<?>> packetsToBeHandled = Queues.newConcurrentLinkedQueue();
|
||||
+ private final Queue<PacketProcessor.ListenerAndPacket<?>> packetsToBeHandled = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>(); // Camellia - Replace packet processing queue with mt-queue
|
||||
private final Thread runningThread;
|
||||
private boolean closed;
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 17 Dec 2025 00:20:57 +0800
|
||||
Subject: [PATCH] Add null check in RegionizedWorldData >> conections
|
||||
|
||||
some of our function patches will crash because of get null in conn.getPlayer(), then when we process player.chunkPosition() will get NPE, so we need to fix it.
|
||||
|
||||
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
index 491d108cc8e440653746f20fc5b5bca3b791eb88..5c6f40f82370b5a6f329dad6109bd4b7209c2e4c 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
@@ -231,14 +231,17 @@ public final class RegionizedWorldData {
|
||||
regionToData.get(CoordinateUtils.getChunkKey(pos.x() >> chunkToRegionShift, pos.z() >> chunkToRegionShift)).itemEntityMovementTrackerMap.put(key, tracker);
|
||||
}
|
||||
// Luminol end
|
||||
+ Set<Connection> cons = new java.util.HashSet<>(from.connections.size()); // Lophine - save all connections we have processed in connection process
|
||||
// connections
|
||||
for (final Connection conn : from.connections) {
|
||||
final ServerPlayer player = conn.getPlayer();
|
||||
+ if (player == null) continue; // Lophine - skip null player connections
|
||||
final ChunkPos pos = player.chunkPosition();
|
||||
// Note: It is impossible for an entity in the world to _not_ be in an entity chunk, which means
|
||||
// the chunk holder must _exist_, and so the region section exists.
|
||||
regionToData.get(CoordinateUtils.getChunkKey(pos.x() >> chunkToRegionShift, pos.z() >> chunkToRegionShift))
|
||||
.connections.add(conn);
|
||||
+ cons.add(conn); // Lophine - save all connections we have processed in connection process
|
||||
}
|
||||
// entities
|
||||
for (final ServerPlayer player : from.localPlayers) {
|
||||
@@ -249,6 +252,13 @@ public final class RegionizedWorldData {
|
||||
into.localPlayers.add(player);
|
||||
player.getBukkitEntity().updateRegion(into);
|
||||
into.nearbyPlayers.addPlayer(player);
|
||||
+ // Lophine start - we need to process the connection if player's connection is not processed, if connection.player is null, next tick will clean up it
|
||||
+ final Connection conn = player.connection.connection;
|
||||
+ if (!cons.contains(conn)) {
|
||||
+ regionToData.get(CoordinateUtils.getChunkKey(pos.x() >> chunkToRegionShift, pos.z() >> chunkToRegionShift))
|
||||
+ .connections.add(conn);
|
||||
+ }
|
||||
+ // Lophine end
|
||||
}
|
||||
for (final Entity entity : from.allEntities) {
|
||||
final ChunkPos pos = entity.chunkPosition();
|
||||
@@ -622,6 +632,7 @@ public final class RegionizedWorldData {
|
||||
private static void cleanUpConnection(final Connection conn) {
|
||||
// note: ALL connections HERE have a player
|
||||
final ServerPlayer player = conn.getPlayer();
|
||||
+ if (player == null) return; // Lophine - skip null player connections
|
||||
// now that the connection is removed, we can allow this region to die
|
||||
player.level().moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(
|
||||
ServerGamePacketListenerImpl.DISCONNECT_TICKET, player.connection.disconnectPos,
|
||||
@@ -35,7 +35,7 @@ index fcdfd4f6de5e1c2d5e80c7f3d8cb0b33a62d257d..6eaa4e9bd1ecbfa531313ce1f470d55e
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
||||
index 45611868dfdfee05380c390a11e8041685a76a19..434dcd0216a217d6f5a7dc008efd069c125c823f 100644
|
||||
index 45611868dfdfee05380c390a11e8041685a76a19..0219692014bd5d7b52afcb847c5cd39c4dc63ff2 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
|
||||
@@ -40,8 +40,8 @@ public class PaperVersionFetcher implements VersionFetcher {
|
||||
@@ -44,8 +44,8 @@ index 45611868dfdfee05380c390a11e8041685a76a19..434dcd0216a217d6f5a7dc008efd069c
|
||||
private static final int DISTANCE_UNKNOWN = -2;
|
||||
- private static final String DOWNLOAD_PAGE = "https://github.com/LuminolMC/Luminol"; // Luminol
|
||||
- private static final String REPOSITORY = "LuminolMC/Luminol"; // Luminol
|
||||
+ private static final String DOWNLOAD_PAGE = "https://github.com/LophineCraft/Lophine"; // Luminol // Lophine
|
||||
+ private static final String REPOSITORY = "LophineCraft/Lophine"; // Luminol // Lophine
|
||||
+ private static final String DOWNLOAD_PAGE = "https://github.com/LophineLabs/Lophine"; // Luminol // Lophine
|
||||
+ private static final String REPOSITORY = "LophineLabs/Lophine"; // Luminol // Lophine
|
||||
private static final ServerBuildInfo BUILD_INFO = ServerBuildInfo.buildInfo();
|
||||
private static final String USER_AGENT = BUILD_INFO.brandName() + "/" + BUILD_INFO.asString(VERSION_SIMPLE) + " (https://papermc.io)";
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@@ -0,0 +1,759 @@
|
||||
package fun.bm.lophine.bot;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.ActionType;
|
||||
import fun.bm.lophine.bot.action.gui.GuiNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.agent.actions.AbstractBotAction;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
import org.leavesmc.leaves.entity.bot.action.BotAction;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftBotAction;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class BotActionGuiContainer extends SimpleContainer {
|
||||
private static final Map<String, GuiRootNode> GUI_ROOT_NODE_MAP = new LinkedHashMap<>();
|
||||
|
||||
private static final int CONTAINER_SIZE = 54;
|
||||
|
||||
private static final int[] BORDER_SLOTS = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 17,
|
||||
18, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43, 44,
|
||||
45, 46, 47, 48, 49, 50, 51, 52, 53
|
||||
};
|
||||
|
||||
public static final int[] CONTENT_SLOTS = {
|
||||
10, 11, 12, 13, 14, 15, 16,
|
||||
19, 20, 21, 22, 23, 24, 25
|
||||
};
|
||||
|
||||
private static final int BACK_BUTTON_SLOT = 38;
|
||||
private static final int COMMAND_BUILDER_SLOT = 40;
|
||||
private static final int HOME_BUTTON_SLOT = 42;
|
||||
private static final int PREV_PAGE_SLOT = 39;
|
||||
private static final int NEXT_PAGE_SLOT = 41;
|
||||
|
||||
private static final ItemStack boarder = new ItemStack(Items.WHITE_STAINED_GLASS_PANE);
|
||||
private static final ItemStack emptyActionPlaceholder = createEmptyActionPlaceholder();
|
||||
|
||||
private final CraftBot bot;
|
||||
private final CraftPlayer player;
|
||||
private final Deque<GuiNode> navigationStack = new ArrayDeque<>();
|
||||
private final Deque<Boolean> fromRootLevelStack = new ArrayDeque<>(); // Track if we came from root level
|
||||
private GuiNode currentNode = null;
|
||||
|
||||
// New state for action type selection flow
|
||||
private ActionType selectedActionType = null;
|
||||
private boolean isSelectingActionType = false;
|
||||
|
||||
// Pagination state
|
||||
private int currentPage = 0;
|
||||
|
||||
public static void registerGuiRootNode(GuiRootNode guiRootNode) {
|
||||
if (!GUI_ROOT_NODE_MAP.containsKey(guiRootNode.getName())) {
|
||||
GUI_ROOT_NODE_MAP.put(guiRootNode.getName(), guiRootNode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregisterGuiRootNode(String name) {
|
||||
GUI_ROOT_NODE_MAP.remove(name);
|
||||
}
|
||||
|
||||
public static GuiRootNode getGuiRootNode(String name) {
|
||||
return GUI_ROOT_NODE_MAP.get(name);
|
||||
}
|
||||
|
||||
public static Map<String, GuiRootNode> getGuiRootNodes() {
|
||||
return new LinkedHashMap<>(GUI_ROOT_NODE_MAP);
|
||||
}
|
||||
|
||||
public BotActionGuiContainer(@NotNull CraftBot bot, CraftPlayer player) {
|
||||
super(CONTAINER_SIZE, player);
|
||||
this.bot = bot;
|
||||
this.player = player;
|
||||
this.showActionTypes();
|
||||
}
|
||||
|
||||
public boolean isCommandBuilderSlot(int id) {
|
||||
return id == COMMAND_BUILDER_SLOT;
|
||||
}
|
||||
|
||||
private void fillBorder() {
|
||||
for (int slot : BORDER_SLOTS) {
|
||||
this.setItem(slot, boarder);
|
||||
}
|
||||
}
|
||||
|
||||
private void showActionTypes() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.selectedActionType = null;
|
||||
this.isSelectingActionType = true;
|
||||
this.currentPage = 0;
|
||||
|
||||
this.renderActionTypes();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
private void showRootNodes() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.isSelectingActionType = false;
|
||||
this.currentPage = 0;
|
||||
|
||||
List<GuiRootNode> allNodes = new ArrayList<>(GUI_ROOT_NODE_MAP.values());
|
||||
if (allNodes.isEmpty()) {
|
||||
// No root nodes, go back to action type selection
|
||||
this.showActionTypes();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderRootNodes();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
public void navigateToChild(GuiNode node) {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're at root level (currentNode is null but not selecting action type),
|
||||
// we need to track that we came from root nodes level
|
||||
if (this.currentNode != null) {
|
||||
this.navigationStack.push(this.currentNode);
|
||||
this.fromRootLevelStack.push(false);
|
||||
} else if (!this.isSelectingActionType && this.selectedActionType != null) {
|
||||
// We're at root nodes level, mark that we should return to root nodes
|
||||
this.fromRootLevelStack.push(true);
|
||||
}
|
||||
|
||||
this.currentNode = node;
|
||||
this.currentPage = 0;
|
||||
this.refreshContainer();
|
||||
}
|
||||
|
||||
public boolean navigateBack() {
|
||||
// If we're at root node level (no navigation stack), go back to action type selection
|
||||
if (this.navigationStack.isEmpty() && this.fromRootLevelStack.isEmpty()) {
|
||||
// If we have a selected action type, we're at the root nodes level
|
||||
if (!this.isSelectingActionType && this.selectedActionType != null) {
|
||||
this.showActionTypes();
|
||||
return true;
|
||||
}
|
||||
// Already at action type selection
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we should return to root nodes level
|
||||
boolean fromRootLevel = !this.fromRootLevelStack.isEmpty() && this.fromRootLevelStack.pop();
|
||||
|
||||
if (fromRootLevel) {
|
||||
// Return to root nodes level
|
||||
this.currentNode = null;
|
||||
this.showRootNodes();
|
||||
} else if (!this.navigationStack.isEmpty()) {
|
||||
// Pop from navigation stack and refresh
|
||||
this.currentNode = this.navigationStack.pop();
|
||||
this.refreshContainer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void navigateHome() {
|
||||
this.showActionTypes();
|
||||
}
|
||||
|
||||
private void refreshContainer() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
if (children == null || children.isEmpty()) {
|
||||
// Empty page: auto-navigate back
|
||||
this.navigateBack();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderCurrentNodeChildren();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<GuiNode> getChildrenOfCurrentNode() {
|
||||
if (this.currentNode == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.currentNode instanceof GuiRootNode rootNode) {
|
||||
return rootNode.getChildren();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderActionTypes() {
|
||||
List<ActionType> allTypes = List.of(ActionType.values());
|
||||
this.renderPagedItems(allTypes, (actionType, slot) -> {
|
||||
ItemStack item = actionType.toConfirmNode(null).getItemStack();
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderRootNodes() {
|
||||
List<GuiRootNode> allNodes = new ArrayList<>(GUI_ROOT_NODE_MAP.values());
|
||||
this.renderPagedItems(allNodes, (node, slot) -> {
|
||||
ItemStack item = createNodeItemWithRunHint(node);
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void renderCurrentNodeChildren() {
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
if (children != null && !children.isEmpty()) {
|
||||
List<GuiNode> childList = new ArrayList<>(children);
|
||||
this.renderPagedItems(childList, (node, slot) -> {
|
||||
ItemStack item = createNodeItemWithRunHint(node);
|
||||
if (item != null && !item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void renderBotActions() {
|
||||
int actionSize = this.bot.getActionSize();
|
||||
if (actionSize > 0) {
|
||||
List<Integer> actionIndices = new ArrayList<>();
|
||||
for (int i = 0; i < actionSize; i++) {
|
||||
actionIndices.add(i);
|
||||
}
|
||||
this.renderPagedItems(actionIndices, (index, slot) -> {
|
||||
BotAction<?> action = this.bot.getAction(index);
|
||||
ItemStack item = createActionItem(action, index);
|
||||
if (!item.isEmpty()) {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Show empty placeholder when no actions are active
|
||||
this.setItem(CONTENT_SLOTS[0], emptyActionPlaceholder);
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemStack createEmptyActionPlaceholder() {
|
||||
ItemStack item = new ItemStack(Items.STRUCTURE_VOID);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§cNo active actions"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(
|
||||
Component.literal("§cThis bot has no running actions"),
|
||||
Component.literal("§cUse START to add new actions")
|
||||
)));
|
||||
return item;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GuiNode getGuiNodeAtSlot(int slot) {
|
||||
if (slot == BACK_BUTTON_SLOT && this.canNavigateBack()) {
|
||||
return null; // back button is handled separately
|
||||
}
|
||||
|
||||
// Check if slot is a border slot
|
||||
for (int borderSlot : BORDER_SLOTS) {
|
||||
if (borderSlot == slot) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Set<GuiNode> nodes = this.getCurrentDisplayNodes();
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the content index for this slot
|
||||
int contentIndex = -1;
|
||||
for (int i = 0; i < CONTENT_SLOTS.length; i++) {
|
||||
if (CONTENT_SLOTS[i] == slot) {
|
||||
contentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Apply pagination offset
|
||||
int actualIndex = this.currentPage * CONTENT_SLOTS.length + contentIndex;
|
||||
int index = 0;
|
||||
for (GuiNode node : nodes) {
|
||||
if (index == actualIndex) {
|
||||
return node;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isBackButtonSlot(int slot) {
|
||||
return slot == BACK_BUTTON_SLOT && this.canNavigateBack();
|
||||
}
|
||||
|
||||
public boolean isHomeButtonSlot(int slot) {
|
||||
return slot == HOME_BUTTON_SLOT && this.canNavigateHome();
|
||||
}
|
||||
|
||||
private static ItemStack createBackButtonItem() {
|
||||
ItemStack item = new ItemStack(Items.RED_WOOL);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§cBack"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(Component.literal("Return to previous page"))));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createHomeButtonItem() {
|
||||
ItemStack item = new ItemStack(Items.RED_BED);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§aHome"));
|
||||
item.set(DataComponents.LORE, new ItemLore(List.of(Component.literal("Return to main menu"))));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ItemStack createNodeItemWithRunHint(GuiNode node) {
|
||||
ItemStack itemStack = node.getItemStack();
|
||||
|
||||
// Check if this is a last-level node (no children)
|
||||
boolean isLastLevel;
|
||||
if (node instanceof GuiRootNode rootNode) {
|
||||
isLastLevel = rootNode.getChildren().isEmpty();
|
||||
} else {
|
||||
// Non-RootNode is always considered last level
|
||||
isLastLevel = true;
|
||||
}
|
||||
|
||||
// If it's the last level and we have an action type selected, add run hint
|
||||
if (isLastLevel && this.selectedActionType != null && !this.isSelectingActionType) {
|
||||
// Get existing lore or create new one
|
||||
ItemLore existingLore = itemStack.get(DataComponents.LORE);
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
|
||||
if (existingLore != null) {
|
||||
loreLines.addAll(existingLore.lines());
|
||||
}
|
||||
|
||||
// Add golden run hint
|
||||
loreLines.add(Component.literal("§6Click to execute"));
|
||||
|
||||
itemStack.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render items with pagination support.
|
||||
*
|
||||
* @param items the full list of items
|
||||
* @param renderer callback to render each item at a specific slot
|
||||
*/
|
||||
private <T> void renderPagedItems(List<T> items, PagedItemRenderer<T> renderer) {
|
||||
int pageSize = CONTENT_SLOTS.length;
|
||||
int totalPages = Math.max(1, (items.size() + pageSize - 1) / pageSize);
|
||||
|
||||
// Clamp current page
|
||||
if (this.currentPage >= totalPages) {
|
||||
this.currentPage = totalPages - 1;
|
||||
}
|
||||
if (this.currentPage < 0) {
|
||||
this.currentPage = 0;
|
||||
}
|
||||
|
||||
int start = this.currentPage * pageSize;
|
||||
int end = Math.min(start + pageSize, items.size());
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
int slotIndex = i - start;
|
||||
renderer.render(items.get(i), CONTENT_SLOTS[slotIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface PagedItemRenderer<T> {
|
||||
void render(T item, int slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add navigation buttons including pagination controls.
|
||||
*/
|
||||
private void addNavigationButtons() {
|
||||
if (this.canNavigateBack()) {
|
||||
this.setItem(BACK_BUTTON_SLOT, createBackButtonItem());
|
||||
}
|
||||
|
||||
this.setItem(COMMAND_BUILDER_SLOT, createCommandBuilderItem());
|
||||
|
||||
if (this.canNavigateHome()) {
|
||||
this.setItem(HOME_BUTTON_SLOT, createHomeButtonItem());
|
||||
}
|
||||
|
||||
// Pagination buttons
|
||||
if (this.hasPrevPage()) {
|
||||
this.setItem(PREV_PAGE_SLOT, createPrevPageItem());
|
||||
}
|
||||
if (this.hasNextPage()) {
|
||||
this.setItem(NEXT_PAGE_SLOT, createNextPageItem());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPrevPage() {
|
||||
return this.currentPage > 0;
|
||||
}
|
||||
|
||||
private boolean hasNextPage() {
|
||||
int totalItems = this.getCurrentTotalItemCount();
|
||||
int totalPages = Math.max(1, (totalItems + CONTENT_SLOTS.length - 1) / CONTENT_SLOTS.length);
|
||||
return this.currentPage < totalPages - 1;
|
||||
}
|
||||
|
||||
private int getCurrentTotalItemCount() {
|
||||
if (this.isSelectingActionType) {
|
||||
return ActionType.values().length;
|
||||
}
|
||||
if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
return this.bot.getActionSize();
|
||||
}
|
||||
if (this.currentNode == null) {
|
||||
return GUI_ROOT_NODE_MAP.size();
|
||||
}
|
||||
Set<GuiNode> children = this.getChildrenOfCurrentNode();
|
||||
return children != null ? children.size() : 0;
|
||||
}
|
||||
|
||||
public void prevPage() {
|
||||
if (this.hasPrevPage()) {
|
||||
this.currentPage--;
|
||||
this.redisplayCurrentViewWithoutReset();
|
||||
}
|
||||
}
|
||||
|
||||
public void nextPage() {
|
||||
if (this.hasNextPage()) {
|
||||
this.currentPage++;
|
||||
this.redisplayCurrentViewWithoutReset();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPrevPageSlot(int slot) {
|
||||
return slot == PREV_PAGE_SLOT && this.hasPrevPage();
|
||||
}
|
||||
|
||||
public boolean isNextPageSlot(int slot) {
|
||||
return slot == NEXT_PAGE_SLOT && this.hasNextPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-render the current view without resetting pagination state.
|
||||
* Used for page navigation to preserve currentPage value.
|
||||
*/
|
||||
private void redisplayCurrentViewWithoutReset() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
|
||||
if (this.isSelectingActionType) {
|
||||
this.renderActionTypes();
|
||||
} else if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
this.renderBotActions();
|
||||
} else if (this.currentNode != null) {
|
||||
this.renderCurrentNodeChildren();
|
||||
} else {
|
||||
this.renderRootNodes();
|
||||
}
|
||||
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
private static ItemStack createPrevPageItem() {
|
||||
ItemStack item = new ItemStack(Items.ARROW);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§ePrevious Page"));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createNextPageItem() {
|
||||
ItemStack item = new ItemStack(Items.ARROW);
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§eNext Page"));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ItemStack createCommandBuilderItem() {
|
||||
ItemStack item = new ItemStack(Items.BOOK);
|
||||
|
||||
// Build the complete command preview
|
||||
String commandPreview = buildCommandPreview();
|
||||
|
||||
// Check if current node is runnable (confirmable)
|
||||
boolean canRun = this.currentNode != null && this.currentNode.isConfirmable();
|
||||
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
loreLines.add(Component.literal("§eCommand:"));
|
||||
loreLines.add(Component.literal("§f" + commandPreview));
|
||||
|
||||
// Add run hint if the node is confirmable
|
||||
if (canRun) {
|
||||
loreLines.add(Component.literal("§6Click to execute"));
|
||||
}
|
||||
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§6Command Builder"));
|
||||
item.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
return item;
|
||||
}
|
||||
|
||||
private String buildCommandPreview() {
|
||||
if (this.selectedActionType == null) {
|
||||
return "Select an action type first";
|
||||
}
|
||||
|
||||
// For STOP action type, show different preview
|
||||
if (this.selectedActionType == ActionType.ACTION_STOP) {
|
||||
int actionSize = this.bot.getActionSize();
|
||||
if (actionSize == 0) {
|
||||
return "No actions to stop";
|
||||
}
|
||||
return "Click an action to stop it (" + actionSize + " active)";
|
||||
}
|
||||
|
||||
try {
|
||||
String actionPrefix = this.selectedActionType.getCommandActionPrefix();
|
||||
String actionSuffix = this.selectedActionType.getCommandActionSuffix();
|
||||
if (!actionSuffix.isEmpty()) {
|
||||
actionSuffix = actionSuffix + " ";
|
||||
}
|
||||
|
||||
String extra = actionPrefix + " " + this.bot.getName() + " " + actionSuffix;
|
||||
|
||||
// If we have a current node and it's a GuiRootNode, build the command
|
||||
if (this.currentNode instanceof GuiRootNode rootNode) {
|
||||
String command = rootNode.buildCommand(extra);
|
||||
|
||||
// Apply parameter limit based on ActionType
|
||||
if (this.selectedActionType.getMaxAllowedParameters() == 0) {
|
||||
command = actionPrefix + " " + this.bot.getName();
|
||||
}
|
||||
|
||||
return command;
|
||||
} else if (!this.isSelectingActionType) {
|
||||
// At root nodes level or selecting action type
|
||||
return extra.trim() + " <command>";
|
||||
} else {
|
||||
return "Select a command node";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "Error building command";
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<GuiNode> getCurrentDisplayNodes() {
|
||||
// If selecting action type, return null (handled separately)
|
||||
if (this.isSelectingActionType) {
|
||||
return null;
|
||||
}
|
||||
if (this.currentNode == null) {
|
||||
return new LinkedHashSet<>(GUI_ROOT_NODE_MAP.values());
|
||||
}
|
||||
return this.getChildrenOfCurrentNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user selects an ActionType (START/STOP)
|
||||
*/
|
||||
public void selectActionType(ActionType actionType) {
|
||||
this.selectedActionType = actionType;
|
||||
this.isSelectingActionType = false;
|
||||
|
||||
// If STOP action type is selected, show current bot actions instead of root nodes
|
||||
if (actionType == ActionType.ACTION_STOP) {
|
||||
this.showCurrentBotActions();
|
||||
} else {
|
||||
this.showRootNodes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the current bot's scheduled actions for stopping
|
||||
*/
|
||||
public void showCurrentBotActions() {
|
||||
this.clearContent();
|
||||
this.fillBorder();
|
||||
this.currentNode = null;
|
||||
this.navigationStack.clear();
|
||||
this.fromRootLevelStack.clear();
|
||||
this.isSelectingActionType = false;
|
||||
this.currentPage = 0;
|
||||
|
||||
this.renderBotActions();
|
||||
this.addNavigationButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ItemStack representing a bot action for display in the GUI
|
||||
*/
|
||||
private ItemStack createActionItem(BotAction<?> action, int index) {
|
||||
// Use the same item model as registered in GuiRootNode (start action display)
|
||||
AbstractBotAction<?> handle = ((CraftBotAction<?, ?>) action).getHandle();
|
||||
GuiRootNode guiData = handle.getGuiData();
|
||||
ItemStack item = guiData != null ? guiData.getItemStack() : new ItemStack(Items.PAPER);
|
||||
String actionName = action.getName();
|
||||
String actionHash = action.getUUID().toString();
|
||||
|
||||
item.set(DataComponents.CUSTOM_NAME, Component.literal("§c" + actionName));
|
||||
|
||||
// Store action hash in CUSTOM_DATA for later retrieval
|
||||
CompoundTag customTag = new CompoundTag();
|
||||
customTag.putString("action_hash", actionHash);
|
||||
item.set(DataComponents.CUSTOM_DATA, CustomData.of(customTag));
|
||||
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
((CraftBotAction<?, ?>) action).getHandle().save(nbt);
|
||||
nbt.forEach((key, tag) -> loreLines.add(Component.literal("§7" + key + ": §f" + tag)));
|
||||
loreLines.add(Component.literal("§7Index: §f" + index));
|
||||
loreLines.add(Component.literal("§7Hash: §f" + actionHash.substring(0, 8)));
|
||||
loreLines.add(Component.literal("§6Click to stop this action"));
|
||||
|
||||
item.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action hash (UUID string) at a specific slot (for STOP action type)
|
||||
* Returns null if not found or not in STOP mode
|
||||
*/
|
||||
@Nullable
|
||||
public String getActionHashAtSlot(int slot) {
|
||||
if (this.selectedActionType != ActionType.ACTION_STOP) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read the action hash directly from the ItemStack's CUSTOM_DATA
|
||||
ItemStack item = this.getItem(slot);
|
||||
if (item.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CustomData customData = item.get(DataComponents.CUSTOM_DATA);
|
||||
if (customData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompoundTag tag = customData.copyTag();
|
||||
if (tag.contains("action_hash")) {
|
||||
return tag.getString("action_hash").get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ActionType at a specific slot, accounting for pagination offset.
|
||||
* Returns null if the slot is not a valid content slot or the index is out of bounds.
|
||||
*/
|
||||
@Nullable
|
||||
public ActionType getActionTypeAtSlot(int slot) {
|
||||
if (!this.isSelectingActionType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int contentIndex = -1;
|
||||
for (int i = 0; i < CONTENT_SLOTS.length; i++) {
|
||||
if (CONTENT_SLOTS[i] == slot) {
|
||||
contentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contentIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ActionType[] actionTypes = ActionType.values();
|
||||
int actualIndex = this.currentPage * CONTENT_SLOTS.length + contentIndex;
|
||||
if (actualIndex >= 0 && actualIndex < actionTypes.length) {
|
||||
return actionTypes[actualIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected ActionType
|
||||
*/
|
||||
@Nullable
|
||||
public ActionType getSelectedActionType() {
|
||||
return this.selectedActionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're in action type selection mode
|
||||
*/
|
||||
public boolean isSelectingActionType() {
|
||||
return this.isSelectingActionType;
|
||||
}
|
||||
|
||||
public boolean canNavigateBack() {
|
||||
return !this.navigationStack.isEmpty() || (!this.isSelectingActionType && this.selectedActionType != null);
|
||||
}
|
||||
|
||||
public boolean canNavigateHome() {
|
||||
return !this.isSelectingActionType && (this.currentNode != null || this.selectedActionType != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the command builder can be executed.
|
||||
* Returns true if there is a current node and it is confirmable.
|
||||
*/
|
||||
public boolean canExecuteCommandBuilder() {
|
||||
return this.currentNode != null && this.currentNode.isConfirmable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GuiNode getCurrentNode() {
|
||||
return this.currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current parameter count (number of selected command nodes).
|
||||
* This equals the navigation stack size plus 1 if currentNode is not null.
|
||||
*/
|
||||
public int getCurrentParameterCount() {
|
||||
return this.navigationStack.size() + (this.currentNode != null ? 1 : 0);
|
||||
}
|
||||
|
||||
public CraftBot getBot() {
|
||||
return this.bot;
|
||||
}
|
||||
|
||||
public CraftPlayer getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NotNull Player player) {
|
||||
return this.player.getHandle() == player && this.bot.isValid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package fun.bm.lophine.bot;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.bot.action.gui.ActionType;
|
||||
import fun.bm.lophine.bot.action.gui.GuiNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.entity.bot.CraftBot;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
|
||||
public class BotActionGuiMenu extends AbstractContainerMenu {
|
||||
private final BotActionGuiContainer container;
|
||||
private final CraftBot bot;
|
||||
private final CraftPlayer player;
|
||||
private CraftInventoryView view = null;
|
||||
|
||||
public BotActionGuiMenu(int containerId, Inventory inventory, BotActionGuiContainer container) {
|
||||
super(MenuType.GENERIC_9x6, containerId);
|
||||
this.container = container;
|
||||
this.bot = container.getBot();
|
||||
this.player = container.getPlayer();
|
||||
|
||||
for (int row = 0; row < 6; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(container, col + row * 9, 8 + col * 18, 18 + row * 18) {
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col + row * 9 + 9, 8 + col * 18, 140 + row * 18));
|
||||
}
|
||||
}
|
||||
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(inventory, col, 8 + col * 18, 198));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.InventoryView getBukkitView() {
|
||||
if (this.view == null) {
|
||||
CraftInventory inventory = new CraftInventory(this.container);
|
||||
this.view = new CraftInventoryView(
|
||||
this.player,
|
||||
inventory,
|
||||
this
|
||||
);
|
||||
}
|
||||
return this.view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(@NotNull Player player) {
|
||||
return this.container.stillValid(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int slotIndex, int buttonNum, ContainerInput containerInput, Player player) {
|
||||
if (slotIndex >= 0 && slotIndex < 54) {
|
||||
if (this.container.isBackButtonSlot(slotIndex)) {
|
||||
this.container.navigateBack();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.container.isHomeButtonSlot(slotIndex)) {
|
||||
this.container.navigateHome();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle pagination buttons
|
||||
if (this.container.isPrevPageSlot(slotIndex)) {
|
||||
this.container.prevPage();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
if (this.container.isNextPageSlot(slotIndex)) {
|
||||
this.container.nextPage();
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle command builder click
|
||||
if (this.container.isCommandBuilderSlot(slotIndex) && this.container.canExecuteCommandBuilder()) {
|
||||
this.executeCommandBuilder(player);
|
||||
this.refreshSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle ActionType selection (with pagination offset)
|
||||
if (this.container.isSelectingActionType()) {
|
||||
ActionType clickedType = this.container.getActionTypeAtSlot(slotIndex);
|
||||
if (clickedType != null) {
|
||||
this.container.selectActionType(clickedType);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle action stop click (when STOP action type is selected)
|
||||
if (this.container.getSelectedActionType() == ActionType.ACTION_STOP) {
|
||||
String actionHash = this.container.getActionHashAtSlot(slotIndex);
|
||||
if (actionHash != null) {
|
||||
this.stopActionByHash(actionHash, player);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GuiNode node = this.container.getGuiNodeAtSlot(slotIndex);
|
||||
if (node != null) {
|
||||
this.handleNodeClick(node, player);
|
||||
this.refreshSlots();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
super.clicked(slotIndex, buttonNum, containerInput, player);
|
||||
}
|
||||
|
||||
private void handleNodeClick(GuiNode node, Player player) {
|
||||
if (node instanceof GuiRootNode rootNode) {
|
||||
// Get the selected action type
|
||||
ActionType actionType = this.container.getSelectedActionType();
|
||||
if (actionType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxAllowedParameters = actionType.getMaxAllowedParameters();
|
||||
|
||||
// If the node has children, check parameter limit before navigating
|
||||
if (!rootNode.getChildren().isEmpty()) {
|
||||
// Calculate current parameter count (navigation stack size + 1 for current node if exists)
|
||||
int currentParamCount = this.container.getCurrentParameterCount();
|
||||
|
||||
// If adding this node would exceed the limit, execute command instead of navigating
|
||||
if (currentParamCount + 1 > maxAllowedParameters) {
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType == ActionType.ACTION_START && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
} else {
|
||||
this.container.navigateToChild(rootNode);
|
||||
}
|
||||
} else {
|
||||
// Execute command with t`he selected action type
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType == ActionType.ACTION_START && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeCommand(GuiRootNode node, ActionType actionType, Player player) {
|
||||
try {
|
||||
String actionPrefix = actionType.getCommandActionPrefix();
|
||||
String actionSuffix = actionType.getCommandActionSuffix();
|
||||
if (!actionSuffix.isEmpty()) {
|
||||
actionSuffix = actionSuffix + " ";
|
||||
}
|
||||
|
||||
String extra = actionPrefix + " " + bot.getName() + " " + actionSuffix;
|
||||
String command = node.buildCommand(extra);
|
||||
|
||||
// Apply parameter limit based on ActionType
|
||||
if (actionType.getMaxAllowedParameters() == 0) {
|
||||
// For actions like STOP that don't allow parameters, trim to just "action botName"
|
||||
command = actionPrefix + " " + bot.getName();
|
||||
}
|
||||
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
MinecraftServer.getServer().getCommands().performPrefixedCommand(
|
||||
serverPlayer.createCommandSourceStack(),
|
||||
command
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Error executing command: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command from the command builder (book item).
|
||||
* Uses the current node and selected action type.
|
||||
*/
|
||||
private void executeCommandBuilder(Player player) {
|
||||
GuiNode currentNode = this.container.getCurrentNode();
|
||||
ActionType actionType = this.container.getSelectedActionType();
|
||||
|
||||
if (currentNode instanceof GuiRootNode rootNode && actionType != null) {
|
||||
this.executeCommand(rootNode, actionType, player);
|
||||
// Close GUI after START action execution
|
||||
if (actionType != ActionType.ACTION_STOP && player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.closeContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a bot action by its hash (UUID string)
|
||||
*/
|
||||
private void stopActionByHash(String actionHash, Player player) {
|
||||
try {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
String command = getStopActionCommand(actionHash);
|
||||
MinecraftServer.getServer().getCommands().performPrefixedCommand(
|
||||
serverPlayer.createCommandSourceStack(),
|
||||
command
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Error stopping action with hash {}: ", actionHash, e);
|
||||
}
|
||||
// Always refresh to show updated action list after stop attempt
|
||||
this.container.showCurrentBotActions();
|
||||
}
|
||||
|
||||
private String getStopActionCommand(String actionHash) throws UnexpectedException {
|
||||
boolean botCommand = FakeplayerConfig.enable;
|
||||
boolean playerCommand = FakePlayerCompatConfig.commandPlayer;
|
||||
String command;
|
||||
if (botCommand) {
|
||||
command = "bot ";
|
||||
} else if (playerCommand) {
|
||||
command = "player ";
|
||||
} else {
|
||||
throw new UnexpectedException("Unable to build String from commandNode.");
|
||||
}
|
||||
command = command + "action " + this.bot.getName() + " stop " + actionHash;
|
||||
return command;
|
||||
}
|
||||
|
||||
private void refreshSlots() {
|
||||
for (int i = 0; i < 54; i++) {
|
||||
Slot slot = this.slots.get(i);
|
||||
ItemStack item = this.container.getItem(i);
|
||||
slot.set(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public BotActionGuiContainer getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum ActionType {
|
||||
ACTION_START("Start", "Start a action", Items.LIME_DYE, "action", "start"),
|
||||
ACTION_STOP("Stop", "Stop a action or see this bot scheduled tasks", Items.RED_DYE, "action", "stop", 0);
|
||||
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
private final Item item;
|
||||
private final String commandActionPrefix;
|
||||
private final String commandActionSuffix;
|
||||
private final int maxAllowedParameters;
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix) {
|
||||
this(displayName, description, item, commandActionPrefix, "", Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix, String commandActionSuffix) {
|
||||
this(displayName, description, item, commandActionPrefix, commandActionSuffix, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
ActionType(String displayName, String description, Item item, String commandActionPrefix, String commandActionSuffix, int maxAllowedParameters) {
|
||||
this.displayName = displayName;
|
||||
this.description = description;
|
||||
this.item = item;
|
||||
this.commandActionPrefix = commandActionPrefix;
|
||||
this.commandActionSuffix = commandActionSuffix;
|
||||
this.maxAllowedParameters = maxAllowedParameters;
|
||||
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public String getCommandActionPrefix() {
|
||||
return this.commandActionPrefix;
|
||||
}
|
||||
|
||||
public String getCommandActionSuffix() {
|
||||
return commandActionSuffix;
|
||||
}
|
||||
|
||||
public GuiNode toConfirmNode(GuiRootNode targetNode) {
|
||||
return new ActionConfirmNode(this.displayName, this.description, this.item, targetNode, this.commandActionPrefix, this.maxAllowedParameters);
|
||||
}
|
||||
|
||||
public int getMaxAllowedParameters() {
|
||||
return maxAllowedParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* A special node used in the action type confirmation UI.
|
||||
* It holds a reference to the target GuiRootNode and the selected action string.
|
||||
*/
|
||||
public static class ActionConfirmNode extends GuiNode {
|
||||
private final GuiRootNode targetNode;
|
||||
private final String action;
|
||||
private final int maxAllowedParameters;
|
||||
|
||||
public ActionConfirmNode(String name, String description, Item item, GuiRootNode targetNode, String action, int maxAllowedParameters) {
|
||||
super(name, description, item);
|
||||
this.targetNode = targetNode;
|
||||
this.action = action;
|
||||
this.maxAllowedParameters = maxAllowedParameters;
|
||||
}
|
||||
|
||||
public GuiRootNode getTargetNode() {
|
||||
return this.targetNode;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public int getMaxAllowedParameters() {
|
||||
return this.maxAllowedParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
ItemStack itemStack = super.getItemStack();
|
||||
|
||||
// Add parameter limit info to lore
|
||||
List<Component> loreLines = new ArrayList<>();
|
||||
if (this.description != null && !this.description.isEmpty()) {
|
||||
loreLines.add(Component.literal(this.description));
|
||||
}
|
||||
|
||||
if (!loreLines.isEmpty()) {
|
||||
itemStack.set(DataComponents.LORE, new ItemLore(loreLines));
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.ItemLore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GuiNode {
|
||||
protected final String name;
|
||||
protected final String description;
|
||||
protected final Item item;
|
||||
protected final boolean confirmable;
|
||||
|
||||
private static final Item defaultItem = Items.PAPER;
|
||||
|
||||
public GuiNode(String name, String description, Item item, boolean confirmable) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.item = item;
|
||||
this.confirmable = confirmable;
|
||||
}
|
||||
|
||||
public GuiNode(String name, String description, Item item) {
|
||||
this(name, description, item, true);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public boolean isConfirmable() {
|
||||
return this.confirmable;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack() {
|
||||
ItemStack itemStack = new ItemStack(this.item == null ? defaultItem : this.item).copy();
|
||||
itemStack.set(DataComponents.CUSTOM_NAME, Component.literal(this.name));
|
||||
if (this.description != null && !this.description.isEmpty()) {
|
||||
ItemLore lore = new ItemLore(List.of(Component.literal(this.description)));
|
||||
itemStack.set(DataComponents.LORE, lore);
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GuiRootNode extends GuiNode {
|
||||
protected final Set<GuiNode> children;
|
||||
protected final String commandNode;
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, String commandNode) {
|
||||
this(name, description, item, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, String commandNode, boolean confirmable) {
|
||||
this(name, description, item, new LinkedHashSet<>(), commandNode, confirmable);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, Set<GuiNode> children, String commandNode) {
|
||||
this(name, description, item, children, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiRootNode(String name, String description, Item item, Set<GuiNode> children, String commandNode, boolean confirmable) {
|
||||
super(name, description, item, confirmable);
|
||||
this.children = children;
|
||||
this.commandNode = commandNode;
|
||||
}
|
||||
|
||||
public final void child(GuiNode... node) {
|
||||
this.children.addAll(List.of(node));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void child(Supplier<? extends GuiNode>... node) {
|
||||
this.children.addAll(Stream.of(node).map(Supplier::get).toList());
|
||||
}
|
||||
|
||||
public Set<GuiNode> getChildren() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
public Set<GuiNode> getAllFurthestChildren() {
|
||||
if (this.children.isEmpty()) {
|
||||
Set<GuiNode> result = new LinkedHashSet<>();
|
||||
result.add(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<GuiNode> furthestChildren = new LinkedHashSet<>();
|
||||
for (GuiNode child : this.children) {
|
||||
furthestChildren.addAll(((GuiRootNode) child).getAllFurthestChildren());
|
||||
}
|
||||
return furthestChildren;
|
||||
}
|
||||
|
||||
public String getCommandNode() {
|
||||
return this.commandNode;
|
||||
}
|
||||
|
||||
public String buildCommand(String extra) throws UnexpectedException {
|
||||
boolean botCommand = FakeplayerConfig.enable;
|
||||
boolean playerCommand = FakePlayerCompatConfig.commandPlayer;
|
||||
if (botCommand) {
|
||||
return "bot " + extra + this.getCommandNode();
|
||||
}
|
||||
if (playerCommand) {
|
||||
return "player " + extra + this.getCommandNode();
|
||||
}
|
||||
throw new UnexpectedException("Unable to build String from commandNode.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package fun.bm.lophine.bot.action.gui;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.rmi.UnexpectedException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GuiSubNode extends GuiRootNode {
|
||||
protected final GuiNode parent;
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, Set<GuiNode> children, String commandNode) {
|
||||
this(name, description, item, parent, children, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, Set<GuiNode> children, String commandNode, boolean confirmable) {
|
||||
super(name, description, item, children, commandNode, confirmable);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, String commandNode) {
|
||||
this(name, description, item, parent, commandNode, true);
|
||||
}
|
||||
|
||||
public GuiSubNode(String name, String description, Item item, GuiNode parent, String commandNode, boolean confirmable) {
|
||||
this(name, description, item, parent, new LinkedHashSet<>(), commandNode, confirmable);
|
||||
}
|
||||
|
||||
public GuiNode getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildCommand(String extra) throws UnexpectedException {
|
||||
return ((GuiRootNode) parent).buildCommand(extra) + " " + getCommandNode();
|
||||
}
|
||||
}
|
||||
+5
@@ -40,6 +40,11 @@ public class FakeplayerConfig implements IConfigModule {
|
||||
Regeneration amount for fakeplayers""")
|
||||
public static double regenAmount = 0.0;
|
||||
|
||||
@ConfigInfo(name = "open-action-gui", comments = """
|
||||
Allow opening fakeplayer action gui,
|
||||
need sneak to open if you enabled inventory open gui""")
|
||||
public static boolean canOpenActionGui = false;
|
||||
|
||||
@ConfigInfo(name = "use-action", comments = """
|
||||
Allow fakeplayers to use actions""")
|
||||
public static boolean canUseAction = true;
|
||||
|
||||
@@ -20,6 +20,8 @@ package org.leavesmc.leaves.bot;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import fun.bm.lophine.LophineLogger;
|
||||
import fun.bm.lophine.bot.BotActionGuiContainer;
|
||||
import fun.bm.lophine.bot.BotActionGuiMenu;
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
@@ -357,15 +359,28 @@ public class ServerBot extends ServerPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InteractionResult interact(@NotNull Player player, @NotNull InteractionHand hand, @NotNull net.minecraft.world.phys.Vec3 location) { // Leaves - Paper 26.1: Entity#interact now takes Vec3
|
||||
if (FakePlayerCompatConfig.openFakePlayerInventory) {
|
||||
if (player instanceof ServerPlayer player1 && player.getMainHandItem().isEmpty()) {
|
||||
public @NotNull InteractionResult interact(@NotNull Player player, @NotNull InteractionHand hand, @NotNull net.minecraft.world.phys.Vec3 location) {
|
||||
if (player instanceof ServerPlayer player1 && player.getMainHandItem().isEmpty()) {
|
||||
boolean isSneaking = player.isShiftKeyDown();
|
||||
boolean enabled1Only = FakePlayerCompatConfig.openFakePlayerInventory ^ FakeplayerConfig.canOpenActionGui;
|
||||
boolean openInventory = enabled1Only ? FakePlayerCompatConfig.openFakePlayerInventory : FakePlayerCompatConfig.openFakePlayerInventory && !isSneaking;
|
||||
boolean openActionGui = enabled1Only ? FakeplayerConfig.canOpenActionGui : FakeplayerConfig.canOpenActionGui && isSneaking;
|
||||
|
||||
if (openInventory) {
|
||||
BotInventoryOpenEvent event = new BotInventoryOpenEvent(this.getBukkitEntity(), player1.getBukkitEntity());
|
||||
getServer().server.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
player.openMenu(new SimpleMenuProvider((i, inventory, p) -> ChestMenu.sixRows(i, inventory, this.container), this.getDisplayName()));
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
} else if (openActionGui) {
|
||||
BotActionGuiOpenEvent event = new BotActionGuiOpenEvent(this.getBukkitEntity(), player1.getBukkitEntity());
|
||||
getServer().server.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
BotActionGuiContainer container = new BotActionGuiContainer(this.getBukkitEntity(), player1.getBukkitEntity());
|
||||
player.openMenu(new SimpleMenuProvider((i, inventory, p) -> new BotActionGuiMenu(i, inventory, container), this.getDisplayName()));
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.interact(player, hand, location);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent;
|
||||
|
||||
import fun.bm.lophine.bot.BotActionGuiContainer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -59,6 +60,9 @@ public class Actions {
|
||||
if (!actionsByName.containsKey(action.getName())) {
|
||||
actionsByName.put(action.getName(), action);
|
||||
actionsByClass.put(type, action);
|
||||
if (action.getGuiData() != null) {
|
||||
BotActionGuiContainer.registerGuiRootNode(action.getGuiData());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -70,6 +74,8 @@ public class Actions {
|
||||
|
||||
public static boolean unregister(@NotNull String name) {
|
||||
AbstractBotAction<?> action = actionsByName.remove(name);
|
||||
BotActionGuiContainer.unregisterGuiRootNode(name);
|
||||
|
||||
if (action != null) {
|
||||
actionsByClass.remove(action.getClass());
|
||||
return true;
|
||||
|
||||
+8
@@ -20,10 +20,12 @@ package org.leavesmc.leaves.bot.agent.actions;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import fun.bm.lophine.LophineLogger;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.ExtraData;
|
||||
import org.leavesmc.leaves.command.CommandContext;
|
||||
@@ -57,6 +59,8 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
|
||||
private Consumer<E> onSuccess;
|
||||
private Consumer<E> onStop;
|
||||
|
||||
protected GuiRootNode guiData;
|
||||
|
||||
public AbstractBotAction(String name, Supplier<E> creator) {
|
||||
this.name = name;
|
||||
this.uuid = UUID.randomUUID();
|
||||
@@ -261,4 +265,8 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
|
||||
public void setOnStop(Consumer<E> onStop) {
|
||||
this.onStop = onStop;
|
||||
}
|
||||
|
||||
public @Nullable GuiRootNode getGuiData() {
|
||||
return this.guiData;
|
||||
}
|
||||
}
|
||||
|
||||
+29
-3
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiSubNode;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.agent.ExtraData;
|
||||
@@ -29,13 +31,37 @@ import static org.leavesmc.leaves.command.ArgumentNode.ArgumentSuggestions.strin
|
||||
|
||||
public abstract class AbstractTimerBotAction<E extends AbstractTimerBotAction<E>> extends AbstractBotAction<E> {
|
||||
|
||||
public AbstractTimerBotAction(String name, Supplier<E> creator) {
|
||||
public AbstractTimerBotAction(String name, Supplier<E> creator, GuiRootNode guiData) {
|
||||
super(name, creator);
|
||||
this.addArgument("delay", integer(0)).suggests(strings("0", "5", "10", "20")).setOptional(true);
|
||||
this.addArgument("interval", integer(0)).suggests(strings("20", "0", "5", "10")).setOptional(true);
|
||||
String[] delaySuggestions = {"0", "5", "10", "20"};
|
||||
String[] intervalSuggestions = {"20", "0", "5", "10"};
|
||||
this.addArgument("delay", integer(0)).suggests(strings(delaySuggestions)).setOptional(true);
|
||||
this.addArgument("interval", integer(0)).suggests(strings(intervalSuggestions)).setOptional(true);
|
||||
this.addArgument("do_number", integer(-1))
|
||||
.suggests(((context, builder) -> builder.suggest("-1", Component.literal("do infinite times"))))
|
||||
.setOptional(true);
|
||||
|
||||
if (guiData == null) return;
|
||||
|
||||
this.guiData = guiData;
|
||||
GuiSubNode[] node1 = new GuiSubNode[delaySuggestions.length];
|
||||
for (int i = 0; i < delaySuggestions.length; i++) {
|
||||
node1[i] = new GuiSubNode(delaySuggestions[i], "Delay for a few ticks", null, guiData, delaySuggestions[i]);
|
||||
guiData.child(node1[i]);
|
||||
}
|
||||
GuiSubNode[] node2 = new GuiSubNode[delaySuggestions.length * intervalSuggestions.length];
|
||||
for (int i = 0; i < delaySuggestions.length; i++) {
|
||||
for (int j = 0; j < intervalSuggestions.length; j++) {
|
||||
int id = i * intervalSuggestions.length + j;
|
||||
node2[id] = new GuiSubNode(intervalSuggestions[j], "Interval for a few ticks", null, node1[i], intervalSuggestions[j]);
|
||||
node1[i].child(node2[id]);
|
||||
}
|
||||
}
|
||||
|
||||
for (GuiSubNode node : node2) {
|
||||
GuiSubNode subNode = new GuiSubNode("-1", "Number of times to do", null, node, "-1");
|
||||
node.child(subNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-2
@@ -17,6 +17,9 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import fun.bm.lophine.bot.action.gui.GuiSubNode;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
@@ -35,8 +38,8 @@ public abstract class AbstractUseBotAction<T extends AbstractUseBotAction<T>> ex
|
||||
private int alreadyUsedTick = 0;
|
||||
private int useItemRemainingTicks = 0;
|
||||
|
||||
public AbstractUseBotAction(String name, Supplier<T> supplier) {
|
||||
super(name, supplier);
|
||||
public AbstractUseBotAction(String name, Supplier<T> supplier, GuiRootNode guiData) {
|
||||
super(name, supplier, guiData);
|
||||
this.addArgument("use_timeout", integer(-1))
|
||||
.suggests((context, builder) -> {
|
||||
builder.suggest("-1", Component.literal("no use timeout"));
|
||||
@@ -44,6 +47,17 @@ public abstract class AbstractUseBotAction<T extends AbstractUseBotAction<T>> ex
|
||||
builder.suggest("10", Component.literal("minimum trident shoot time"));
|
||||
})
|
||||
.setOptional(true);
|
||||
|
||||
if (guiData == null) return;
|
||||
|
||||
for (GuiNode node : guiData.getAllFurthestChildren()) {
|
||||
GuiSubNode node0 = (GuiSubNode) node;
|
||||
node0.child(
|
||||
new GuiSubNode("-1", "no use timeout", null, node0, "-1"),
|
||||
new GuiSubNode("3", "minimum bow shoot time", null, node0, "3"),
|
||||
new GuiSubNode("10", "minimum trident shoot time", null, node0, "10")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
@@ -25,7 +27,8 @@ import org.leavesmc.leaves.entity.bot.actions.CraftAttackAction;
|
||||
public class ServerAttackAction extends AbstractTimerBotAction<ServerAttackAction> {
|
||||
|
||||
public ServerAttackAction() {
|
||||
super("attack", ServerAttackAction::new);
|
||||
GuiRootNode guiData = new GuiRootNode("Attack", "Attack an entity", Items.DIAMOND_SWORD, "attack");
|
||||
super("attack", ServerAttackAction::new, guiData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-1
@@ -17,10 +17,12 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -33,7 +35,8 @@ import org.leavesmc.leaves.entity.bot.actions.CraftBreakBlockAction;
|
||||
public class ServerBreakBlockAction extends AbstractTimerBotAction<ServerBreakBlockAction> {
|
||||
|
||||
public ServerBreakBlockAction() {
|
||||
super("break", ServerBreakBlockAction::new);
|
||||
GuiRootNode guiRootNode = new GuiRootNode("Break", "Break a block", Items.DIAMOND_PICKAXE, "break");
|
||||
super("break", ServerBreakBlockAction::new, guiRootNode);
|
||||
}
|
||||
|
||||
private ItemStack lastItem = null;
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftDropAction;
|
||||
@@ -24,7 +26,8 @@ import org.leavesmc.leaves.entity.bot.actions.CraftDropAction;
|
||||
public class ServerDropAction extends AbstractTimerBotAction<ServerDropAction> {
|
||||
|
||||
public ServerDropAction() {
|
||||
super("drop", ServerDropAction::new);
|
||||
GuiRootNode guiRootNode = new GuiRootNode("Drop", "Drop all items", Items.BARRIER, "drop");
|
||||
super("drop", ServerDropAction::new, guiRootNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-1
@@ -17,11 +17,13 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.projectile.FishingHook;
|
||||
import net.minecraft.world.item.FishingRodItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftFishAction;
|
||||
@@ -29,7 +31,8 @@ import org.leavesmc.leaves.entity.bot.actions.CraftFishAction;
|
||||
public class ServerFishAction extends AbstractTimerBotAction<ServerFishAction> {
|
||||
|
||||
public ServerFishAction() {
|
||||
super("fish", ServerFishAction::new);
|
||||
GuiRootNode guiRootNode = new GuiRootNode("Fish", "Fish", Items.FISHING_ROD, "fish");
|
||||
super("fish", ServerFishAction::new, guiRootNode);
|
||||
}
|
||||
|
||||
private static final int CATCH_ENTITY_DELAY = 20;
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftJumpAction;
|
||||
@@ -24,7 +26,8 @@ import org.leavesmc.leaves.entity.bot.actions.CraftJumpAction;
|
||||
public class ServerJumpAction extends AbstractTimerBotAction<ServerJumpAction> {
|
||||
|
||||
public ServerJumpAction() {
|
||||
super("jump", ServerJumpAction::new);
|
||||
GuiRootNode guiRootNode = new GuiRootNode("Jump", "Jump", Items.ELYTRA, "jump");
|
||||
super("jump", ServerJumpAction::new, guiRootNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.entity.CraftVehicle;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
@@ -31,6 +33,8 @@ public class ServerMountAction extends AbstractBotAction<ServerMountAction> {
|
||||
|
||||
public ServerMountAction() {
|
||||
super("mount", ServerMountAction::new);
|
||||
|
||||
this.guiData = new GuiRootNode("Mount", "Mount", Items.SADDLE, "mount");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.bot.agent.ExtraData;
|
||||
@@ -32,6 +34,11 @@ public class ServerMoveAction extends AbstractStateBotAction<ServerMoveAction> {
|
||||
public ServerMoveAction() {
|
||||
super("move", ServerMoveAction::new);
|
||||
this.addArgument("direction", EnumArgumentType.fromEnum(MoveDirection.class));
|
||||
|
||||
this.guiData = new GuiRootNode("Move", "Move", null, "move", false);
|
||||
for (MoveDirection direction : MoveDirection.values()) {
|
||||
this.guiData.child(new GuiRootNode(direction.name, direction.name, Items.LEATHER_BOOTS, "move " + direction.name));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftSneakAction;
|
||||
@@ -26,6 +27,8 @@ public class ServerSneakAction extends AbstractStateBotAction<ServerSneakAction>
|
||||
|
||||
public ServerSneakAction() {
|
||||
super("sneak", ServerSneakAction::new);
|
||||
|
||||
this.guiData = new GuiRootNode("Sneak", "Sneak", null, "sneak");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -17,8 +17,10 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
import org.leavesmc.leaves.entity.bot.actions.CraftSwapAction;
|
||||
@@ -27,6 +29,8 @@ public class ServerSwapAction extends AbstractBotAction<ServerSwapAction> {
|
||||
|
||||
public ServerSwapAction() {
|
||||
super("swap", ServerSwapAction::new);
|
||||
|
||||
this.guiData = new GuiRootNode("Swap", "Swap", Items.SHIELD, "swap");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.bot.ServerBot;
|
||||
@@ -26,6 +28,8 @@ public class ServerSwimAction extends AbstractStateBotAction<ServerSwimAction> {
|
||||
|
||||
public ServerSwimAction() {
|
||||
super("swim", ServerSwimAction::new);
|
||||
|
||||
this.guiData = new GuiRootNode("Swim", "Swim", Items.WATER_BUCKET, "swim");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import org.leavesmc.leaves.entity.bot.actions.CraftUseItemAction;
|
||||
public class ServerUseItemAction extends AbstractUseBotAction<ServerUseItemAction> {
|
||||
|
||||
public ServerUseItemAction() {
|
||||
super("use", ServerUseItemAction::new);
|
||||
super("use", ServerUseItemAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-1
@@ -17,10 +17,12 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.bot.action.gui.GuiRootNode;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@@ -37,7 +39,8 @@ import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemToAction.useIte
|
||||
public class ServerUseItemAutoAction extends AbstractUseBotAction<ServerUseItemAutoAction> {
|
||||
|
||||
public ServerUseItemAutoAction() {
|
||||
super("use_auto", ServerUseItemAutoAction::new);
|
||||
GuiRootNode guiRootNode = new GuiRootNode("Use", "Use Item", Items.BOW, "use_auto");
|
||||
super("use_auto", ServerUseItemAutoAction::new, guiRootNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemAction.useItem;
|
||||
public class ServerUseItemOffhandAction extends AbstractUseBotAction<ServerUseItemOffhandAction> {
|
||||
|
||||
public ServerUseItemOffhandAction() {
|
||||
super("use_offhand", ServerUseItemOffhandAction::new);
|
||||
super("use_offhand", ServerUseItemOffhandAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import org.leavesmc.leaves.entity.bot.actions.CraftUseItemOnAction;
|
||||
public class ServerUseItemOnAction extends AbstractUseBotAction<ServerUseItemOnAction> {
|
||||
|
||||
public ServerUseItemOnAction() {
|
||||
super("use_on", ServerUseItemOnAction::new);
|
||||
super("use_on", ServerUseItemOnAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemOnAction.useIte
|
||||
public class ServerUseItemOnOffhandAction extends AbstractUseBotAction<ServerUseItemOnOffhandAction> {
|
||||
|
||||
public ServerUseItemOnOffhandAction() {
|
||||
super("use_on_offhand", ServerUseItemOnOffhandAction::new);
|
||||
super("use_on_offhand", ServerUseItemOnOffhandAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import org.leavesmc.leaves.entity.bot.actions.CraftUseItemToAction;
|
||||
public class ServerUseItemToAction extends AbstractUseBotAction<ServerUseItemToAction> {
|
||||
|
||||
public ServerUseItemToAction() {
|
||||
super("use_to", ServerUseItemToAction::new);
|
||||
super("use_to", ServerUseItemToAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import static org.leavesmc.leaves.bot.agent.actions.ServerUseItemToAction.useIte
|
||||
public class ServerUseItemToOffhandAction extends AbstractUseBotAction<ServerUseItemToOffhandAction> {
|
||||
|
||||
public ServerUseItemToOffhandAction() {
|
||||
super("use_to_offhand", ServerUseItemToOffhandAction::new);
|
||||
super("use_to_offhand", ServerUseItemToOffhandAction::new, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+43
@@ -18,6 +18,7 @@
|
||||
package org.leavesmc.leaves.command.bot.subcommands.action;
|
||||
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
@@ -49,6 +50,7 @@ public class StopCommand extends LiteralNode {
|
||||
public StopCommand() {
|
||||
super("stop");
|
||||
children(StopIndexArgument::new);
|
||||
children(StopHashArgument::new);
|
||||
children(StopAll::new);
|
||||
}
|
||||
|
||||
@@ -105,6 +107,47 @@ public class StopCommand extends LiteralNode {
|
||||
}
|
||||
}
|
||||
|
||||
private static class StopHashArgument extends ArgumentNode<String> {
|
||||
|
||||
private StopHashArgument() {
|
||||
super("hash", StringArgumentType.string());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean execute(CommandContext context) throws CommandSyntaxException {
|
||||
ServerBot bot = ActionCommand.BotArgument.getBot(context);
|
||||
CommandSender sender = context.getSender();
|
||||
|
||||
String hash = context.getArgument(StopHashArgument.class);
|
||||
|
||||
AbstractBotAction<?> action = null;
|
||||
for (AbstractBotAction<?> action1 : bot.getBotActions()) {
|
||||
if (action1.getUUID().toString().equals(hash)) {
|
||||
action = action1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (action == null) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
|
||||
BotActionStopEvent event = new BotActionStopEvent(
|
||||
bot.getBukkitEntity(), action.getName(), action.getUUID(), BotActionStopEvent.Reason.COMMAND, sender
|
||||
);
|
||||
event.callEvent();
|
||||
if (!event.isCancelled()) {
|
||||
action.stop(bot, BotActionStopEvent.Reason.COMMAND);
|
||||
bot.getBotActions().remove(action);
|
||||
sender.sendMessage(join(spaces(),
|
||||
text("Already stopped", GRAY),
|
||||
asAdventure(bot.getDisplayName()).append(text("'s", GRAY)),
|
||||
text("action", GRAY),
|
||||
text(action.getName(), AQUA).hoverEvent(showText(text(action.getActionDataString())))
|
||||
));
|
||||
} else {
|
||||
sender.sendMessage(text("Action stop cancelled by a plugin", RED));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StopAll extends LiteralNode {
|
||||
|
||||
private StopAll() {
|
||||
|
||||
Reference in New Issue
Block a user