做了LuckPerms适配

补充了下指令文档
This commit is contained in:
叁玖领域 2025-12-01 17:29:32 +08:00
parent 03ebcee5c6
commit 014e97662a
8 changed files with 515 additions and 31 deletions

View File

@ -13,6 +13,9 @@ We recommend testing on backups or a development server before deploying to prod
**Super Lead Rope** is a mod that enhances the vanilla lead, allowing players to manage and leash entities more flexibly.
Compared to the original, it is not only more powerful but also supports cross-dimension and even cross-server features.
Super Lead Rope Commands 超级拴绳指令:
[Leash Data Command](./doc/LeashDataCommandUsage_EN) | [超级拴绳数据指令](./doc/LeashDataCommandUsage_CN)
---
## ✨ 功能特色 / Features

View File

@ -76,6 +76,10 @@ repositories {
name = "Curios"
url = "https://maven.theillusivec4.top/"
}
maven {
name = "Luck perms"
url = "https://repo1.maven.org/maven2/net/luckperms/api/"
}
maven {
name = "GeckoLib"
url = "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/"
@ -173,13 +177,12 @@ dependencies {
modRuntimeOnly("curse.maven:spark-361579:4738952")
compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1"))
modImplementation("io.github.llamalad7:mixinextras-forge:0.4.1")
compileOnly("net.luckperms:api:5.4")
compileOnly ('me.lucko:spark-api:0.1-SNAPSHOT')
implementation 'org.ow2.asm:asm:9.6'
implementation 'org.ow2.asm:asm-tree:9.6'
implementation 'org.ow2.asm:asm-commons:9.6'
implementation 'org.ow2.asm:asm-util:9.6'
modCompileOnly("top.r3944realms.lib39:lib39:1.20.1-0.0.15:all@jar")
}
// ===================== Javadoc =====================
@ -445,7 +448,7 @@ publishing {
// ===================== =====================
tasks.named('build') {
dependsOn apiJar, apiJavadocJar
dependsOn sourceJar, apiSourceJar, apiJar, apiJavadocJar
}
tasks.named('clean') {
@ -486,3 +489,30 @@ idea {
downloadJavadoc = true
}
}
tasks.register('showTaskTree') {
doLast {
def showTaskDeps
showTaskDeps = { task, prefix = '', isLast = true ->
//
def connector = isLast ? '└── ' : '├── '
println "${prefix}${connector}${task.name}"
//
def dependencies = task.getTaskDependencies().getDependencies(task).toList()
def newPrefix = prefix + (isLast ? ' ' : '│ ')
dependencies.eachWithIndex { dep, index ->
def lastChild = index == dependencies.size() - 1
showTaskDeps(dep, newPrefix, lastChild)
}
}
def targetTask = tasks.findByName('build')
if (targetTask) {
println "构建任务依赖树:"
showTaskDeps(targetTask, '', true)
} else {
println "未找到 build 任务"
}
}
}

View File

@ -0,0 +1,176 @@
## 基础指令结构
### 主要指令
```
/slp leashdata [子命令]
```
### 指令权限
需要 OP 权限(权限等级 2
## 子命令分类
### 1. GET 命令 - 获取信息
**获取实体拴绳数据:**
```
/slp leashdata get data <targets>
```
- `<targets>`: 目标实体(选择器)
**获取单个实体详细信息:**
```
/slp leashdata get info <target>
```
- `<target>`: 目标实体(选择器)
### 2. ADD 命令 - 添加拴绳
**添加实体拴绳:**
```
/slp leashdata add <targets> <holder> [maxDistance] [elasticDistanceScale] [keepTicks] [reserved]
```
- `<targets>`: 被拴绳实体
- `<holder>`: 持有者实体
- `[maxDistance]`: 最大距离(默认值范围)
- `[elasticDistanceScale]`: 弹性距离比例(默认值范围)
- `[keepTicks]`: 保持时间游戏刻≥0
- `[reserved]`: 保留字段(字符串)
**添加方块拴绳:**
```
/slp leashdata add <targets> block <pos> [maxDistance] [elasticDistanceScale] [keepTicks] [reserved]
```
- `<pos>`: 方块位置
- 其他参数同上
### 3. REMOVE 命令 - 移除拴绳
**移除特定实体拴绳:**
```
/slp leashdata remove <targets> <holder>
```
- `<targets>`: 目标实体
- `<holder>`: 持有者实体
**移除方块拴绳:**
```
/slp leashdata remove <targets> block <pos>
```
- `<pos>`: 方块位置
**批量移除:**
```
/slp leashdata remove <targets> all # 移除所有拴绳
/slp leashdata remove <targets> holders # 移除所有实体拴绳
/slp leashdata remove <targets> knots # 移除所有方块拴绳
```
### 4. TRANSFER 命令 - 转移拴绳
**实体到实体转移:**
```
/slp leashdata transfer <targets> <from> <to> [reserved]
```
- `<from>`: 原持有者
- `<to>`: 新持有者
**方块到实体转移:**
```
/slp leashdata transfer <targets> fromBlock <fromPos> <to> [reserved]
```
- `<fromPos>`: 原方块位置
- `<to>`: 新持有者实体
**方块到方块转移:**
```
/slp leashdata transfer <targets> fromBlock <fromPos> toBlock <toPos> [reserved]
```
- `<fromPos>`: 原方块位置
- `<toPos>`: 新方块位置
### 5. SET 命令 - 设置属性
**设置静态属性(适用于所有拴绳):**
```
/slp leashdata set <targets> static maxDistance reset # 重置最大距离
/slp leashdata set <targets> static maxDistance <value> # 设置最大距离
/slp leashdata set <targets> static elasticDistanceScale reset # 重置弹性比例
/slp leashdata set <targets> static elasticDistanceScale <value> # 设置弹性比例
```
**设置实体拴绳属性:**
```
# 设置最大距离
/slp leashdata set <targets> entity maxDistance <holder> [distance] [keepTicks] [reserved]
/slp leashdata set <targets> entity maxDistance [distance] [keepTicks] [reserved] # 所有持有者
# 设置弹性距离比例
/slp leashdata set <targets> entity elasticDistanceScale <holder> [scale] [keepTicks] [reserved]
/slp leashdata set <targets> entity elasticDistanceScale [scale] [keepTicks] [reserved] # 所有持有者
```
**设置方块拴绳属性:**
```
# 设置最大距离
/slp leashdata set <targets> block <pos> maxDistance [distance] [keepTicks] [reserved]
/slp leashdata set <targets> block maxDistance [distance] [keepTicks] [reserved] # 所有节点
# 设置弹性距离比例
/slp leashdata set <targets> block <pos> elasticDistanceScale [scale] [keepTicks] [reserved]
/slp leashdata set <targets> block elasticDistanceScale [scale] [keepTicks] [reserved] # 所有节点
```
### 6. APPLY FORCES 命令 - 应用物理力
**对实体应用拴绳力:**
```
/slp leashdata applyForces <targets>
```
## 参数说明
### 选择器参数:
- `<targets>`: 实体选择器(如 `@e[type=!player]`, `@a`, `@p`
- `<holder>`, `<from>`, `<to>`: 单个实体选择器
### 数值参数:
- `maxDistance`: 最大距离(`Double`范围LeashConfigManager.MAX_DISTANCE_MIN_VALUE 到 MAX_DISTANCE_MAX_VALUE
- `elasticDistanceScale`: 弹性距离比例(`Double`范围LeashConfigManager.ELASTIC_DISTANCE_MIN_VALUE 到 ELASTIC_DISTANCE_MAX_VALUE
- `keepTicks`: 保持时间(`Integer`≥0
### 其他参数:
- `reserved`: 保留字段(`String`
- `<pos>`, `<fromPos>`, `<toPos>`: 方块位置坐标
## 使用示例
1. **拴住所有羊到玩家:**
```
/slp leashdata add @e[type=sheep] @p 10.0 0.8 600 "farm"
```
2. **查看玩家拴绳状态:**
```
/slp leashdata get info @p
```
3. **将拴绳从木桩转移到玩家:**
```
/slp leashdata transfer @e[type=sheep] fromBlock 100 64 100 @p
```
4. **设置所有拴绳的最大距离:**
```
/slp leashdata set @e[type=sheep] static maxDistance 15.0
```
## 配置默认值
- 最大距离范围:`LeashConfigManager.MAX_DISTANCE_MIN_VALUE` 到 `MAX_DISTANCE_MAX_VALUE`
- 弹性比例范围:`LeashConfigManager.ELASTIC_DISTANCE_MIN_VALUE` 到 `ELASTIC_DISTANCE_MAX_VALUE`
## 注意事项
1. 所有指令需要 OP 权限(权限等级 2
2. 方块拴绳需要在对应位置存在 SuperLeashKnotEntity
3. 转移操作不会作用于来源实体自身(避免循环)
4. 显示结果会限制最多显示 4 个实体(超过会显示省略号)

View File

@ -0,0 +1,176 @@
## Basic Command Structure
### Main Command
```
/slp leashdata [subcommand]
```
### Command Permissions
Requires OP permissions (permission level 2)
## Subcommand Categories
### 1. GET Commands - Retrieve Information
**Get leash data for entities:**
```
/slp leashdata get data <targets>
```
- `<targets>`: Target entities (selector)
**Get detailed info for a single entity:**
```
/slp leashdata get info <target>
```
- `<target>`: Target entity (selector)
### 2. ADD Commands - Add Leashes
**Add entity leash:**
```
/slp leashdata add <targets> <holder> [maxDistance] [elasticDistanceScale] [keepTicks] [reserved]
```
- `<targets>`: Entities to be leashed
- `<holder>`: Holder entity
- `[maxDistance]`: Maximum distance (default value range)
- `[elasticDistanceScale]`: Elastic distance scale (default value range)
- `[keepTicks]`: Keep duration (game ticks, ≥0)
- `[reserved]`: Reserved field (string)
**Add block leash:**
```
/slp leashdata add <targets> block <pos> [maxDistance] [elasticDistanceScale] [keepTicks] [reserved]
```
- `<pos>`: Block position
- Other parameters same as above
### 3. REMOVE Commands - Remove Leashes
**Remove specific entity leash:**
```
/slp leashdata remove <targets> <holder>
```
- `<targets>`: Target entities
- `<holder>`: Holder entity
**Remove block leash:**
```
/slp leashdata remove <targets> block <pos>
```
- `<pos>`: Block position
**Batch removal:**
```
/slp leashdata remove <targets> all # Remove all leashes
/slp leashdata remove <targets> holders # Remove all entity leashes
/slp leashdata remove <targets> knots # Remove all block leashes
```
### 4. TRANSFER Commands - Transfer Leashes
**Transfer from entity to entity:**
```
/slp leashdata transfer <targets> <from> <to> [reserved]
```
- `<from>`: Original holder
- `<to>`: New holder
**Transfer from block to entity:**
```
/slp leashdata transfer <targets> fromBlock <fromPos> <to> [reserved]
```
- `<fromPos>`: Original block position
- `<to>`: New holder entity
**Transfer from block to block:**
```
/slp leashdata transfer <targets> fromBlock <fromPos> toBlock <toPos> [reserved]
```
- `<fromPos>`: Original block position
- `<toPos>`: New block position
### 5. SET Commands - Set Properties
**Set static properties (applies to all leashes):**
```
/slp leashdata set <targets> static maxDistance reset # Reset max distance
/slp leashdata set <targets> static maxDistance <value> # Set max distance
/slp leashdata set <targets> static elasticDistanceScale reset # Reset elastic scale
/slp leashdata set <targets> static elasticDistanceScale <value> # Set elastic scale
```
**Set entity leash properties:**
```
# Set max distance
/slp leashdata set <targets> entity maxDistance <holder> [distance] [keepTicks] [reserved]
/slp leashdata set <targets> entity maxDistance [distance] [keepTicks] [reserved] # All holders
# Set elastic distance scale
/slp leashdata set <targets> entity elasticDistanceScale <holder> [scale] [keepTicks] [reserved]
/slp leashdata set <targets> entity elasticDistanceScale [scale] [keepTicks] [reserved] # All holders
```
**Set block leash properties:**
```
# Set max distance
/slp leashdata set <targets> block <pos> maxDistance [distance] [keepTicks] [reserved]
/slp leashdata set <targets> block maxDistance [distance] [keepTicks] [reserved] # All knots
# Set elastic distance scale
/slp leashdata set <targets> block <pos> elasticDistanceScale [scale] [keepTicks] [reserved]
/slp leashdata set <targets> block elasticDistanceScale [scale] [keepTicks] [reserved] # All knots
```
### 6. APPLY FORCES Command - Apply Physics Forces
**Apply leash forces to entities:**
```
/slp leashdata applyForces <targets>
```
## Parameter Descriptions
### Selector Parameters:
- `<targets>`: Entity selector (e.g., `@e[type=!player]`, `@a`, `@p`)
- `<holder>`, `<from>`, `<to>`: Single entity selector
### Numerical Parameters:
- `maxDistance`: Maximum distance (`Double`, range: LeashConfigManager.MAX_DISTANCE_MIN_VALUE to MAX_DISTANCE_MAX_VALUE)
- `elasticDistanceScale`: Elastic distance scale (`Double`, range: LeashConfigManager.ELASTIC_DISTANCE_MIN_VALUE to ELASTIC_DISTANCE_MAX_VALUE)
- `keepTicks`: Keep duration (`Integer`, ≥0)
### Other Parameters:
- `reserved`: Reserved field (`String`)
- `<pos>`, `<fromPos>`, `<toPos>`: Block position coordinates
## Usage Examples
1. **Leash all sheep to player:**
```
/slp leashdata add @e[type=sheep] @p 10.0 0.8 600 "farm"
```
2. **Check player's leash status:**
```
/slp leashdata get info @p
```
3. **Transfer leash from fence post to player:**
```
/slp leashdata transfer @e[type=sheep] fromBlock 100 64 100 @p
```
4. **Set max distance for all leashes:**
```
/slp leashdata set @e[type=sheep] static maxDistance 15.0
```
## Configuration Defaults
- Max distance range: `LeashConfigManager.MAX_DISTANCE_MIN_VALUE` to `MAX_DISTANCE_MAX_VALUE`
- Elastic scale range: `LeashConfigManager.ELASTIC_DISTANCE_MIN_VALUE` to `ELASTIC_DISTANCE_MAX_VALUE`
## Important Notes
1. All commands require OP permissions (permission level 2)
2. Block leashes require SuperLeashKnotEntity to exist at the target position
3. Transfer operations do not affect the source entity itself (to prevent loops)
4. Display results are limited to showing up to 4 entities (shows ellipsis for more)

View File

@ -22,10 +22,7 @@ import net.minecraftforge.fml.ModList;
/**
* The type Curtain compat.
*/
public class CurtainCompat {
/**
* The constant isModLoaded.
*/
public class CurtainCompat{
public final static boolean isModLoaded = ModList.get().isLoaded("curtain");
/**

View File

@ -0,0 +1,95 @@
package top.r3944realms.superleadrope.compat;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.context.*;
import net.luckperms.api.node.Node;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.fml.ModList;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.superleadrope.SuperLeadRope;
import top.r3944realms.superleadrope.util.capability.LeashDataInnerAPI;
import java.util.Objects;
public class LuckPermsCompat {
public final static boolean isModLoaded = ModList.get().isLoaded("luckperms");
public static volatile ILPC instance;
public interface ILPC {
void init();
default boolean isLeashedBypass(Entity player) { return false; }
}
@Contract(" -> new")
public static @NotNull ILPC getOrCreateLPC() {
if (instance == null) {
synchronized (LuckPermsCompat.class) {
if (instance == null) {
if (!isModLoaded) {
instance = new DummyLPC();
} else instance = new RealLPC();
}
}
}
return instance;
}
// 空实现
private static class DummyLPC implements ILPC {
@Override
public void init() {}
}
// 真实实现只有在模组加载时才被初始化
private static class RealLPC implements ILPC {
private boolean isInitialized;
private LuckPerms luckPerms ;
private final Node LeashBypass = Node.builder(SuperLeadRope.MOD_ID + ".leash.bypass").build();
public RealLPC() {
isInitialized = false;
init();
}
@Override
public void init() {
try {
luckPerms = LuckPermsProvider.get();
luckPerms.getContextManager().registerCalculator(new LeashCalculator());
isInitialized = true;
} catch (IllegalStateException e) {
SuperLeadRope.logger.error("LuckPermsCompat failed to initialize", e);
}
}
@Override
public boolean isLeashedBypass(Entity player) {
if (!(player instanceof Player)) return false;
return isInitialized && luckPerms.getUserManager().isLoaded(player.getUUID()) &&
Objects.requireNonNull(luckPerms.getUserManager().getUser(player.getUUID()))
.getNodes()
.stream()
.filter(i -> i.equals(LeashBypass))
.findFirst()
.map(Node::getValue)
.orElse(false);
}
public static class LeashCalculator implements ContextCalculator<Player> {
@Override
public void calculate(@NotNull Player target, ContextConsumer contextConsumer) {
contextConsumer.accept("isLeashed", String.valueOf(LeashDataInnerAPI.QueryOperations.hasLeash(target)));
}
@Override
public @NotNull ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = ImmutableContextSet.builder();
builder.add("isLeashed", "false");
builder.add("isLeashed", "true");
return builder.build();
}
}
}
}

View File

@ -44,6 +44,7 @@ import top.r3944realms.superleadrope.api.event.SuperLeadRopeEvent;
import top.r3944realms.superleadrope.api.type.capabilty.ILeashData;
import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo;
import top.r3944realms.superleadrope.compat.CurtainCompat;
import top.r3944realms.superleadrope.compat.LuckPermsCompat;
import top.r3944realms.superleadrope.config.LeashConfigManager;
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
@ -1607,7 +1608,7 @@ public class LeashDataImpl implements ILeashData {
}
public boolean canBeAttachedTo(Entity pEntity) {
if (pEntity == entity) {
if (pEntity == entity && !LuckPermsCompat.getOrCreateLPC().isLeashedBypass(entity)) {
return false;
} else {
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity);

View File

@ -13,48 +13,54 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI');
var Opcodes = Java.type("org.objectweb.asm.Opcodes");
var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
var InsnNode = Java.type("org.objectweb.asm.tree.InsnNode");
function initializeCoreMod() {
return {
"leash_render_patch": {
"target": {
"type": "METHOD",
"class": "net.minecraft.client.renderer.entity.MobRenderer",
"methodName": "m_5523_",
"methodDesc": "(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;DDD)Z"
'leash_render': {
'target': {
'type': 'METHOD',
'class': 'net.minecraft.client.renderer.entity.MobRenderer',
'methodName': ASMAPI.mapMethod('m_5523_'), // shouldRender
'methodDesc': '(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;DDD)Z'
},
"transformer": function(method) {
'transformer': function(method) {
var insns = method.instructions;
// 寻找具体的 ICONST_0 位置
for (var i = 0; i < insns.size(); i++) {
var insn = insns.get(i);
if (insn.getOpcode && insn.getOpcode() === Opcodes.ICONST_0) {
var next = insns.get(i + 1);
if (next && next.getOpcode() === Opcodes.IRETURN) {
// 插入调试日志和方法调用
insns.insertBefore(insn, ASMAPI.listOf(
new VarInsnNode(Opcodes.ALOAD, 1), // Mob
new VarInsnNode(Opcodes.ALOAD, 2), // Frustum
ASMAPI.buildMethodCall(
// 寻找 L4 标签后的 ICONST_0 -> IRETURN 序列
if (insn.getOpcode() === Opcodes.ICONST_0) {
var nextInsn = insns.get(i + 1);
if (nextInsn && nextInsn.getOpcode() === Opcodes.IRETURN) {
// 找到目标位置,插入我们的钩子调用
var newInstructions = ASMAPI.listOf(
new VarInsnNode(Opcodes.ALOAD, 1), // 加载 Mob 参数 (livingEntity)
new VarInsnNode(Opcodes.ALOAD, 2), // 加载 Frustum 参数 (camera)
new MethodInsnNode(
Opcodes.INVOKESTATIC,
'top/r3944realms/superleadrope/core/hook/LeashRenderHook',
null,
ASMAPI.MethodType.STATIC,
'shouldRenderExtra',
'(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;)Z',
ASMAPI.MethodCallMode.STATIC
false
)
));
// 移除原来的 ICONST_0
insns.remove(insn);
);
// 在 ICONST_0 之前插入新指令,然后移除 ICONST_0
method.instructions.insertBefore(insn, newInstructions);
method.instructions.remove(insn);
// 只需要修改这一个地方
break;
}
}
}
return method;
}
}