feat: 完善CommandNode,使其支持权限控制输出以及兼容管理器初始化方法控制触发完善
This commit is contained in:
parent
fc28b6846b
commit
8ebdccc830
192
.github/workflows/build.yml
vendored
192
.github/workflows/build.yml
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
name: Build
|
name: Build and Release
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -10,7 +16,6 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
fetch-tags: true
|
|
||||||
|
|
||||||
- name: Setup JDK 21
|
- name: Setup JDK 21
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -18,8 +23,185 @@ jobs:
|
||||||
java-version: '21'
|
java-version: '21'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Make gradlew executable
|
||||||
uses: gradle/actions/setup-gradle@v4
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Prepare release files
|
||||||
|
run: |
|
||||||
|
mkdir -p release-files
|
||||||
|
cp build/libs/*.jar release-files/ 2>/dev/null || true
|
||||||
|
echo "准备发布的文件:"
|
||||||
|
ls -la release-files/
|
||||||
|
|
||||||
|
- name: Upload release artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-files
|
||||||
|
path: release-files/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout with full history
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-files
|
||||||
|
path: ./dist
|
||||||
|
|
||||||
|
- name: Generate CZ-compliant changelog
|
||||||
|
id: generate_changelog
|
||||||
|
run: |
|
||||||
|
CURRENT_TAG="${{ github.ref_name }}"
|
||||||
|
PREV_TAG=$(git describe --tags --abbrev=0 $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# 创建临时文件
|
||||||
|
TEMP_FILE=$(mktemp)
|
||||||
|
|
||||||
|
echo "# 🚀 版本 $CURRENT_TAG 发布" > $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "## 📋 变更摘要" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
if [ -z "$PREV_TAG" ]; then
|
||||||
|
echo "### 初始版本发布" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "这是项目的第一个正式版本。" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
# 获取所有提交并按类型分组
|
||||||
|
git log --pretty=format:"%s" --reverse | while read -r line; do
|
||||||
|
echo "- $line" >> $TEMP_FILE
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "### 从 $PREV_TAG 到 $CURRENT_TAG 的变更" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
# 定义符合CZ规范的提交类型映射
|
||||||
|
declare -A commit_types
|
||||||
|
commit_types=(
|
||||||
|
["✨ 新功能"]="^(feat|feature)(\(.*\))?:"
|
||||||
|
["🐛 修复"]="^(fix|bugfix)(\(.*\))?:"
|
||||||
|
["📝 文档"]="^(docs|documentation)(\(.*\))?:"
|
||||||
|
["🎨 样式"]="^(style)(\(.*\))?:"
|
||||||
|
["🔨 重构"]="^(refactor)(\(.*\))?:"
|
||||||
|
["⚡️ 性能"]="^(perf|performance)(\(.*\))?:"
|
||||||
|
["✅ 测试"]="^(test)(\(.*\))?:"
|
||||||
|
["🔧 构建"]="^(build)(\(.*\))?:"
|
||||||
|
["👷 CI"]="^(ci)(\(.*\))?:"
|
||||||
|
["📦 依赖"]="^(chore|deps)(\(.*\))?:"
|
||||||
|
["⏪ 回退"]="^(revert)(\(.*\))?:"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取所有提交
|
||||||
|
COMMITS=$(git log --pretty=format:"%s" $PREV_TAG..HEAD)
|
||||||
|
|
||||||
|
# 处理每种类型的提交
|
||||||
|
for type_name in "${!commit_types[@]}"; do
|
||||||
|
pattern="${commit_types[$type_name]}"
|
||||||
|
|
||||||
|
# 提取匹配的提交
|
||||||
|
matched_commits=$(echo "$COMMITS" | grep -E "$pattern" || true)
|
||||||
|
|
||||||
|
if [ -n "$matched_commits" ]; then
|
||||||
|
echo "#### $type_name" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
# 处理每条提交,提取scope和subject
|
||||||
|
echo "$matched_commits" | while read -r commit; do
|
||||||
|
# 解析scope和subject
|
||||||
|
if [[ $commit =~ ^[a-z]+\((.*)\):\ (.*) ]]; then
|
||||||
|
scope="${BASH_REMATCH[1]}"
|
||||||
|
subject="${BASH_REMATCH[2]}"
|
||||||
|
echo "- **$scope**: $subject" >> $TEMP_FILE
|
||||||
|
elif [[ $commit =~ ^[a-z]+:\ (.*) ]]; then
|
||||||
|
subject="${BASH_REMATCH[1]}"
|
||||||
|
echo "- $subject" >> $TEMP_FILE
|
||||||
|
else
|
||||||
|
echo "- $commit" >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 处理破坏性变更(BREAKING CHANGE)
|
||||||
|
breaking_changes=$(git log --pretty=format:"%b" $PREV_TAG..HEAD | grep -i "BREAKING CHANGE" || true)
|
||||||
|
if [ -n "$breaking_changes" ]; then
|
||||||
|
echo "#### ⚠️ 破坏性变更" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "$breaking_changes" | while read -r line; do
|
||||||
|
echo "- $line" >> $TEMP_FILE
|
||||||
|
done
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 处理未分类的提交
|
||||||
|
uncategorized="$COMMITS"
|
||||||
|
for pattern in "${commit_types[@]}"; do
|
||||||
|
uncategorized=$(echo "$uncategorized" | grep -v -E "$pattern" || true)
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$uncategorized" ]; then
|
||||||
|
echo "#### 📝 其他更改" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "$uncategorized" | while read -r commit; do
|
||||||
|
echo "- $commit" >> $TEMP_FILE
|
||||||
|
done
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "## 📊 统计信息" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
if [ -z "$PREV_TAG" ]; then
|
||||||
|
TOTAL_COMMITS=$(git rev-list --count HEAD)
|
||||||
|
echo "- 总提交数: $TOTAL_COMMITS" >> $TEMP_FILE
|
||||||
|
echo "- 首次发布" >> $TEMP_FILE
|
||||||
|
else
|
||||||
|
COMMITS=$(git rev-list --count $PREV_TAG..HEAD)
|
||||||
|
echo "- 本次发布提交数: $COMMITS" >> $TEMP_FILE
|
||||||
|
echo "- 上一个版本: $PREV_TAG" >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
|
||||||
|
echo "- 当前版本: $CURRENT_TAG" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "---" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "### 📜 详细提交历史" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
# 显示所有提交的详细列表,包含scope信息
|
||||||
|
if [ -z "$PREV_TAG" ]; then
|
||||||
|
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short --reverse | head -100 >> $TEMP_FILE
|
||||||
|
else
|
||||||
|
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short $PREV_TAG..HEAD | head -100 >> $TEMP_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 将文件内容输出到变量
|
||||||
|
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
|
||||||
|
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
artifacts: "dist/*.jar"
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
name: "版本 ${{ github.ref_name }}"
|
||||||
|
body: ${{ steps.generate_changelog.outputs.changelog }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
18
README.md
18
README.md
|
|
@ -2,3 +2,21 @@
|
||||||
|
|
||||||
**Lib39** is a general-purpose dependency library for Minecraft mods.
|
**Lib39** is a general-purpose dependency library for Minecraft mods.
|
||||||
It provides utility methods and core functionality that other mods can build upon.
|
It provides utility methods and core functionality that other mods can build upon.
|
||||||
|
### How to implementation?
|
||||||
|
|
||||||
|
**In repositories:**
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
maven {
|
||||||
|
name = "LTD Maven"
|
||||||
|
url = "https://nexus.bot.leisuretimedock.top/repository/maven-public/"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**In dependencies:**
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
dependencies {
|
||||||
|
implementation("top.r3944realms.lib39:lib39:1.20.1-0.4.1")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -33,7 +33,7 @@ mod_name=3944Realms 's Lib Mod
|
||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=MIT
|
mod_license=MIT
|
||||||
# The mod version. See https://semver.org/
|
# The mod version. See https://semver.org/
|
||||||
mod_version=0.3.6
|
mod_version=0.4.1
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package top.r3944realms.lib39.core.command;
|
package top.r3944realms.lib39.core.command;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.network.chat.*;
|
import net.minecraft.network.chat.*;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -435,8 +436,10 @@ public interface ICommandHelpManager {
|
||||||
private void buildCommandTreeString(@NotNull CommandNode node,
|
private void buildCommandTreeString(@NotNull CommandNode node,
|
||||||
@NotNull String indent,
|
@NotNull String indent,
|
||||||
@Nullable String currentFullPath,
|
@Nullable String currentFullPath,
|
||||||
@NotNull List<MutableComponent> result) {
|
@NotNull List<MutableComponent> result,
|
||||||
|
CommandSourceStack commandSourceStack) {
|
||||||
boolean isRoot = indent.isEmpty();
|
boolean isRoot = indent.isEmpty();
|
||||||
|
if (node.testPermission(commandSourceStack)) {
|
||||||
MutableComponent commandLine = buildCommandLine(node, indent, isRoot, currentFullPath);
|
MutableComponent commandLine = buildCommandLine(node, indent, isRoot, currentFullPath);
|
||||||
result.add(commandLine.append(Component.literal(NEWLINE)));
|
result.add(commandLine.append(Component.literal(NEWLINE)));
|
||||||
|
|
||||||
|
|
@ -449,8 +452,8 @@ public interface ICommandHelpManager {
|
||||||
|
|
||||||
for (CommandNode child : node.getChildren()) {
|
for (CommandNode child : node.getChildren()) {
|
||||||
// 只顯示有描述的子命令
|
// 只顯示有描述的子命令
|
||||||
if (!child.getDescription().getString().isEmpty()) {
|
if (!child.getDescription().getString().isEmpty() && node.testPermission(commandSourceStack)) {
|
||||||
buildCommandTreeString(child, childIndent, newFullPath, result);
|
buildCommandTreeString(child, childIndent, newFullPath, result, commandSourceStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (shouldShowCollapsedInfo(node)) {
|
} else if (shouldShowCollapsedInfo(node)) {
|
||||||
|
|
@ -475,15 +478,16 @@ public interface ICommandHelpManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取命令树的字符串表示
|
* 获取命令树的字符串表示
|
||||||
*
|
*
|
||||||
* @return 命令树列表 command tree
|
* @return 命令树列表 command tree
|
||||||
*/
|
*/
|
||||||
default List<MutableComponent> getCommandTree() {
|
default List<MutableComponent> getCommandTree(CommandSourceStack commandSourceStack) {
|
||||||
List<MutableComponent> result = new ArrayList<>();
|
List<MutableComponent> result = new ArrayList<>();
|
||||||
buildCommandTreeString(getRootNode(), "", "", result);
|
buildCommandTreeString(getRootNode(), "", "", result, commandSourceStack);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,8 +539,8 @@ public interface ICommandHelpManager {
|
||||||
*
|
*
|
||||||
* @return the mutable component
|
* @return the mutable component
|
||||||
*/
|
*/
|
||||||
default MutableComponent buildCommandTreeHelp() {
|
default MutableComponent buildCommandTreeHelp(CommandSourceStack commandSourceStack) {
|
||||||
List<MutableComponent> commandTree = getCommandTree();
|
List<MutableComponent> commandTree = getCommandTree(commandSourceStack);
|
||||||
return buildHelpMessage(Component.translatable(Lib39LangKey.Message.HELP_HEADER.getKey(), Component.translatable(getHeadKey())), commandTree);
|
return buildHelpMessage(Component.translatable(Lib39LangKey.Message.HELP_HEADER.getKey(), Component.translatable(getHeadKey())), commandTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ public interface IHelpCommand {
|
||||||
*/
|
*/
|
||||||
default int handleHelp(@NotNull CommandContext<CommandSourceStack> context) {
|
default int handleHelp(@NotNull CommandContext<CommandSourceStack> context) {
|
||||||
ICommandHelpManager commandHelpManager = getCommandHelpManager();
|
ICommandHelpManager commandHelpManager = getCommandHelpManager();
|
||||||
MutableComponent helpMessage = commandHelpManager.buildCommandTreeHelp();
|
MutableComponent helpMessage = commandHelpManager.buildCommandTreeHelp(context.getSource());
|
||||||
sendSuccess(context.getSource(), helpMessage);
|
sendSuccess(context.getSource(), helpMessage);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ public interface IHelpCommand {
|
||||||
ICommandHelpManager commandHelpManager = getCommandHelpManager();
|
ICommandHelpManager commandHelpManager = getCommandHelpManager();
|
||||||
boolean success = commandHelpManager.toggleNodeExpanded(hash);
|
boolean success = commandHelpManager.toggleNodeExpanded(hash);
|
||||||
if (success) {
|
if (success) {
|
||||||
MutableComponent helpMessage = Component.literal("\n".repeat(2)).append(commandHelpManager.buildCommandTreeHelp());
|
MutableComponent helpMessage = Component.literal("\n".repeat(2)).append(commandHelpManager.buildCommandTreeHelp(context.getSource()));
|
||||||
sendSuccess(context.getSource(), helpMessage);
|
sendSuccess(context.getSource(), helpMessage);
|
||||||
} else if (shouldShowToggleFailed()) {
|
} else if (shouldShowToggleFailed()) {
|
||||||
sendFailure(context.getSource(), Component.translatable(Lib39LangKey.Message.HELP_TOGGLE_FAILED.getKey()));
|
sendFailure(context.getSource(), Component.translatable(Lib39LangKey.Message.HELP_TOGGLE_FAILED.getKey()));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package top.r3944realms.lib39.core.command.model;
|
package top.r3944realms.lib39.core.command.model;
|
||||||
|
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
@ -8,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type Command node.
|
* The type Command node.
|
||||||
|
|
@ -18,6 +20,8 @@ public class CommandNode {
|
||||||
private boolean hashValid = false;
|
private boolean hashValid = false;
|
||||||
private final ICommandHelpManager helpManager;
|
private final ICommandHelpManager helpManager;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private final Predicate<CommandSourceStack> testPermission;
|
||||||
|
private static final Predicate<CommandSourceStack> TRUE = i -> true;
|
||||||
private final MutableComponent description;
|
private final MutableComponent description;
|
||||||
private final Map<String, CommandNode> children;
|
private final Map<String, CommandNode> children;
|
||||||
|
|
||||||
|
|
@ -31,6 +35,9 @@ public class CommandNode {
|
||||||
// 展开/闭合状态
|
// 展开/闭合状态
|
||||||
private boolean expanded = true;
|
private boolean expanded = true;
|
||||||
|
|
||||||
|
public boolean testPermission(CommandSourceStack source) {
|
||||||
|
return testPermission.test(source);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Command node.
|
* Instantiates a new Command node.
|
||||||
*
|
*
|
||||||
|
|
@ -40,12 +47,7 @@ public class CommandNode {
|
||||||
* @param isRoot the is root
|
* @param isRoot the is root
|
||||||
*/
|
*/
|
||||||
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description, boolean isRoot) {
|
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description, boolean isRoot) {
|
||||||
this.name = name;
|
this(helpManager, name, description, isRoot, TRUE);
|
||||||
this.helpManager = helpManager;
|
|
||||||
this.description = description;
|
|
||||||
this.children = new TreeMap<>();
|
|
||||||
this.isRoot = isRoot;
|
|
||||||
invalidateHash();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,10 +58,42 @@ public class CommandNode {
|
||||||
* @param description the description
|
* @param description the description
|
||||||
*/
|
*/
|
||||||
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description) {
|
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description) {
|
||||||
|
this(helpManager, name, description, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Command node.
|
||||||
|
*
|
||||||
|
* @param helpManager the help manager
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param isRoot the is root
|
||||||
|
* @param testPermission test has permission
|
||||||
|
*/
|
||||||
|
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description, boolean isRoot, Predicate<CommandSourceStack> testPermission) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.helpManager = helpManager;
|
this.helpManager = helpManager;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.children = new TreeMap<>();
|
this.children = new TreeMap<>();
|
||||||
|
this.isRoot = isRoot;
|
||||||
|
this.testPermission = testPermission;
|
||||||
|
invalidateHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Command node.
|
||||||
|
*
|
||||||
|
* @param helpManager the help manager
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission test has permission
|
||||||
|
*/
|
||||||
|
public CommandNode(ICommandHelpManager helpManager, String name, MutableComponent description, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
this.name = name;
|
||||||
|
this.helpManager = helpManager;
|
||||||
|
this.description = description;
|
||||||
|
this.children = new TreeMap<>();
|
||||||
|
this.testPermission = testPermission;
|
||||||
invalidateHash();
|
invalidateHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,6 +501,36 @@ public class CommandNode {
|
||||||
return root(name, Component.translatable(translationKey));
|
return root(name, Component.translatable(translationKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加根節點
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder root(@NotNull String name, @NotNull MutableComponent description, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
if (root != null) {
|
||||||
|
throw new IllegalStateException("Root node already set");
|
||||||
|
}
|
||||||
|
root = new CommandNode(helpManager, name, description, true, testPermission);
|
||||||
|
nodeStack.push(root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加根節點(使用翻譯鍵)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param translationKey the translation key
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder root(@NotNull String name, @NotNull String translationKey, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
return root(name, Component.translatable(translationKey), testPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 進入子節點(向下移動)
|
* 進入子節點(向下移動)
|
||||||
*
|
*
|
||||||
|
|
@ -552,6 +616,96 @@ public class CommandNode {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 進入子節點(向下移動)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder push(@NotNull String name, @NotNull MutableComponent description, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
checkBuilt();
|
||||||
|
if (nodeStack.isEmpty()) {
|
||||||
|
throw new IllegalStateException("No parent node available. Call root() first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandNode parent = nodeStack.peek();
|
||||||
|
CommandNode child = new CommandNode(helpManager, name, description, testPermission);
|
||||||
|
|
||||||
|
if (!child.isLeaf()) {
|
||||||
|
child.setExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.addChild(child);
|
||||||
|
nodeStack.push(child);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 進入子節點(使用翻譯鍵)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param translationKey the translation key
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder push(@NotNull String name, @NotNull String translationKey, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
return push(name, Component.translatable(translationKey), testPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 進入子節點並添加參數
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param parameters the parameters
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder pushWithParams(@NotNull String name, @NotNull MutableComponent description,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull Parameter... parameters) {
|
||||||
|
push(name, description, testPermission);
|
||||||
|
current().addParameters(parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 進入子節點並添加必填參數
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param paramNames the param names
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder pushWithRequiredParams(@NotNull String name, @NotNull MutableComponent description,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull String @NotNull ... paramNames) {
|
||||||
|
push(name, description, testPermission);
|
||||||
|
for (String paramName : paramNames) {
|
||||||
|
current().addParameter(paramName, true);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 進入子節點並添加可選參數
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param paramNames the param names
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder pushWithOptionalParams(@NotNull String name, @NotNull MutableComponent description,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull String @NotNull ... paramNames) {
|
||||||
|
push(name, description, testPermission);
|
||||||
|
for (String paramName : paramNames) {
|
||||||
|
current().addParameter(paramName, false);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回上一級節點(向上移動)
|
* 返回上一級節點(向上移動)
|
||||||
*
|
*
|
||||||
|
|
@ -711,6 +865,119 @@ public class CommandNode {
|
||||||
return branch(name, Component.translatable(translationKey), configurator);
|
return branch(name, Component.translatable(translationKey), configurator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一個完整的命令分支(鏈式調用)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param configurator the configurator
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder branch(@NotNull String name, @NotNull MutableComponent description,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull BranchConfigurator configurator) {
|
||||||
|
push(name, description, testPermission);
|
||||||
|
configurator.configure(this);
|
||||||
|
pop();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一個完整的命令分支(使用翻譯鍵)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param translationKey the translation key
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param configurator the configurator
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder branch(@NotNull String name, @NotNull String translationKey,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull BranchConfigurator configurator) {
|
||||||
|
return branch(name, Component.translatable(translationKey), testPermission, configurator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速添加葉子節點(沒有子節點的節點)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param description the description
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param parameters the parameters
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder leaf(@NotNull String name, @NotNull MutableComponent description,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull Parameter @NotNull ... parameters) {
|
||||||
|
push(name, description, testPermission);
|
||||||
|
if (parameters.length > 0) {
|
||||||
|
params(parameters);
|
||||||
|
}
|
||||||
|
pop();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速添加葉子節點(使用翻譯鍵)
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param translationKey the translation key
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @param parameters the parameters
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder leaf(@NotNull String name, @NotNull String translationKey,
|
||||||
|
Predicate<CommandSourceStack> testPermission, @NotNull Parameter... parameters) {
|
||||||
|
return leaf(name, Component.translatable(translationKey), testPermission,parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加多個葉子節點
|
||||||
|
*
|
||||||
|
* @param commands the commands
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder leaves(@NotNull Map<String, MutableComponent> commands, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
for (Map.Entry<String, MutableComponent> entry : commands.entrySet()) {
|
||||||
|
leaf(entry.getKey(), entry.getValue(), testPermission);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加多個葉子節點
|
||||||
|
*
|
||||||
|
* @param commands the commands
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder leavesT(@NotNull Map<String, String> commands, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
for (Map.Entry<String, String> entry : commands.entrySet()) {
|
||||||
|
leaf(entry.getKey(), entry.getValue(), testPermission);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加多個帶參數的葉子節點
|
||||||
|
*
|
||||||
|
* @param commands the commands
|
||||||
|
* @param testPermission the test permission
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder leavesWithParams(@NotNull Map<String, LeafConfig> commands, Predicate<CommandSourceStack> testPermission) {
|
||||||
|
for (Map.Entry<String, LeafConfig> entry : commands.entrySet()) {
|
||||||
|
LeafConfig config = entry.getValue();
|
||||||
|
push(entry.getKey(), config.description(), testPermission);
|
||||||
|
if (config.parameters() != null && !config.parameters().isEmpty()) {
|
||||||
|
for (Parameter param : config.parameters()) {
|
||||||
|
param(param.name(), param.required());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 快速添加葉子節點(沒有子節點的節點)
|
* 快速添加葉子節點(沒有子節點的節點)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ public abstract class CompatManager {
|
||||||
|
|
||||||
// 初始化所有兼容模块
|
// 初始化所有兼容模块
|
||||||
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
|
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
|
||||||
if (!entry.getValue().isInitialized()) {
|
if (!entry.getValue().isInitialized() && entry.getValue().isModLoaded()) {
|
||||||
try {
|
try {
|
||||||
entry.getValue().initialize();
|
entry.getValue().initialize();
|
||||||
entry.getValue().setInitialize(true);
|
entry.getValue().setInitialize(true);
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ public class Lib39Compat implements ICompat {
|
||||||
public void addCommonModListener(@NotNull IEventBus modBus) {
|
public void addCommonModListener(@NotNull IEventBus modBus) {
|
||||||
modBus.addListener(this::onSetUp);
|
modBus.addListener(this::onSetUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSetUp (@NotNull FMLCommonSetupEvent event) {
|
private void onSetUp (@NotNull FMLCommonSetupEvent event) {
|
||||||
event.enqueueWork(() -> Lib39.LOGGER.info("Loading Lib39 Compat"));
|
event.enqueueWork(() -> Lib39.LOGGER.info("Loading Lib39 Compat"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
200
src/main/java/top/r3944realms/lib39/util/IUniPosOwner.java
Normal file
200
src/main/java/top/r3944realms/lib39/util/IUniPosOwner.java
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
package top.r3944realms.lib39.util;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个对象接口,该对象可声称对特定维度中的 BlockPos 拥有唯一所有权。
|
||||||
|
* 这个系统被称为 "uniPos",可确保(Level、BlockPos)对在同一时间只能被一个对象 "拥有"。
|
||||||
|
* 一次只能被一个对象 "拥有"。
|
||||||
|
*
|
||||||
|
* <p><b>重要:</b>由于使用了 weakValues(),实现类必须被其他对象强引用,
|
||||||
|
* 否则会被 GC 清理导致锁自动释放。通常这意味着将实现类实例存储在
|
||||||
|
* 适当的管理器或容器中。
|
||||||
|
* @author sch246
|
||||||
|
*/
|
||||||
|
public interface IUniPosOwner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查特定位置当前是否被 *任意* 对象拥有。
|
||||||
|
*
|
||||||
|
* @param level 维度 The level (dimension) of the position.
|
||||||
|
* @param pos 坐标 The position to lock.
|
||||||
|
* @return true if the position has an owner, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isLocked(Level level, BlockPos pos) {
|
||||||
|
return UniPosManager.INSTANCE.getOwner(level, pos) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取位置的当前所有者(如果存在的话)。
|
||||||
|
*
|
||||||
|
* @param level 维度 The level (dimension) of the position.
|
||||||
|
* @param pos 坐标 The position to lock.
|
||||||
|
* @return An Optional containing the owner, or an empty Optional if the position is not owned.
|
||||||
|
*/
|
||||||
|
default Optional<IUniPosOwner> getOwner(Level level, BlockPos pos) {
|
||||||
|
return Optional.ofNullable(UniPosManager.INSTANCE.getOwner(level, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查该对象是否可以锁定指定位置。
|
||||||
|
* 如果该位置当前未锁定,或者该对象已经是所有者,则该值为 true。
|
||||||
|
* 注意:此方法不是原子操作,仅用于快速检查。
|
||||||
|
* 实际锁定时应使用 tryLock() 并检查返回值。
|
||||||
|
*
|
||||||
|
* @param level 维度 The level (dimension) of the position.
|
||||||
|
* @param pos 坐标 The position to lock.
|
||||||
|
* @return true if this object can claim ownership.
|
||||||
|
*/
|
||||||
|
default boolean canLock(Level level, BlockPos pos) {
|
||||||
|
IUniPosOwner owner = UniPosManager.INSTANCE.getOwner(level, pos);
|
||||||
|
return owner == null || owner == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试对该对象的指定位置声明所有权。
|
||||||
|
* 此操作是原子操作。
|
||||||
|
*
|
||||||
|
* @param level 维度 The level (dimension) of the position.
|
||||||
|
* @param pos 坐标 The position to lock.
|
||||||
|
* @return true if ownership was successfully claimed or was already held by this object,
|
||||||
|
* false if the position is owned by another object.
|
||||||
|
*/
|
||||||
|
default boolean tryLock(Level level, BlockPos pos) {
|
||||||
|
return UniPosManager.INSTANCE.tryLock(level, pos, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放指定位置的所有权。
|
||||||
|
* 只有当该对象是当前所有者时,此操作才会成功。
|
||||||
|
*
|
||||||
|
* @param level 位置的级别(维度) The level (dimension) of the position.
|
||||||
|
* @param pos 要解锁的位置 The position to unlock.
|
||||||
|
* @return true if the lock was successfully removed, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean unLock(Level level, BlockPos pos) {
|
||||||
|
return UniPosManager.INSTANCE.unLock(level, pos, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 续租,继续持有锁
|
||||||
|
* @param level 维度
|
||||||
|
* @param pos 位置
|
||||||
|
*/
|
||||||
|
default void refreshLock(Level level, BlockPos pos) {
|
||||||
|
UniPosManager.INSTANCE.refreshLock(level, pos, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
管理 IUniPosOwner 系统的单例存储。
|
||||||
|
该类是后台实现,大多数类不应直接使用。
|
||||||
|
在同一维度键(ResourceKey<Level)范围内,确保任意时刻一个 BlockPos 只能被一个对象“拥有”。
|
||||||
|
注意:锁的作用域是维度键级别,因此在服务端同一维度的不同 Level 实例之间共享。
|
||||||
|
缓存以 BlockPos 的 long 值为键,以 IUniPosOwner 的弱值(weak values)为值;
|
||||||
|
当所有者不再被强引用时,条目会自动移除,从而释放锁。
|
||||||
|
该实现面向服务端使用。
|
||||||
|
*/
|
||||||
|
final class UniPosManager {
|
||||||
|
|
||||||
|
public static final UniPosManager INSTANCE = new UniPosManager();
|
||||||
|
|
||||||
|
// 顶层映射:维度键(ResourceKey<Level>) -> 每维度的坐标缓存。
|
||||||
|
// 缓存键为 BlockPos 的 long 值,值为 IUniPosOwner 的弱值(weak values)。
|
||||||
|
// 使用维度键确保:同一维度的不同 Level 实例在服务端共享同一套锁;
|
||||||
|
// 使用弱值确保:当所有者被 GC 时,锁能自动释放。
|
||||||
|
private final ConcurrentMap<ResourceKey<Level>, Cache<Long, IUniPosOwner>> dimensionLocks;
|
||||||
|
|
||||||
|
private UniPosManager() {
|
||||||
|
this.dimensionLocks = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
获取或创建特定维度键对应的 Cache。
|
||||||
|
Cache 使用 BlockPos 的 long 值作为键,IUniPosOwner 的弱值作为值。
|
||||||
|
按 ResourceKey<Level> 分隔缓存,因此同一维度的不同 Level 实例在服务端共享同一套锁。
|
||||||
|
computeIfAbsent 是原子操作,保证线程安全地获取或创建 Cache。
|
||||||
|
*/
|
||||||
|
private Cache<Long, IUniPosOwner> getDimensionCache(Level level) {
|
||||||
|
// computeIfAbsent 是原子操作,保证线程安全地获取或创建 Cache
|
||||||
|
return dimensionLocks.computeIfAbsent(level.dimension(), k ->
|
||||||
|
CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(5, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取位置的当前所有者。
|
||||||
|
*
|
||||||
|
* @param level 维度
|
||||||
|
* @param pos 坐标
|
||||||
|
* @return 如果存在所有者,则返回所有者对象;否则返回 null。
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public IUniPosOwner getOwner(Level level, BlockPos pos) {
|
||||||
|
return getDimensionCache(level).getIfPresent(pos.asLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试为指定位置设置所有者。
|
||||||
|
* 如果锁已被同一所有者获得或持有。
|
||||||
|
* 此操作是原子操作,避免竞态条件。
|
||||||
|
*
|
||||||
|
* @param level 维度
|
||||||
|
* @param pos 坐标
|
||||||
|
* @param owner 要声明所有权的对象
|
||||||
|
* @return true if the lock was acquired or already held by the same owner, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean tryLock(Level level, BlockPos pos, IUniPosOwner owner) {
|
||||||
|
IUniPosOwner existing = getDimensionCache(level).asMap().putIfAbsent(pos.asLong(), owner);
|
||||||
|
return existing == null || existing == owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解锁一个位置,但前提是所提供的所有者是当前所有者。
|
||||||
|
* 此操作是原子操作。
|
||||||
|
*
|
||||||
|
* @param level 维度
|
||||||
|
* @param pos 要解锁的位置
|
||||||
|
* @param owner 尝试解锁的对象
|
||||||
|
* @return true if the lock was successfully released by this owner.
|
||||||
|
*/
|
||||||
|
public boolean unLock(Level level, BlockPos pos, IUniPosOwner owner) {
|
||||||
|
Cache<Long, IUniPosOwner> cache = dimensionLocks.get(level.dimension());
|
||||||
|
if (cache == null) {
|
||||||
|
return false; // 该维度没有锁
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 asMap() 提供的原子操作
|
||||||
|
return cache.asMap().remove(pos.asLong(), owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 续租,继续持有锁
|
||||||
|
* @param level 维度
|
||||||
|
* @param pos 位置
|
||||||
|
* @param owner 对象
|
||||||
|
*/
|
||||||
|
public void refreshLock(Level level, BlockPos pos, IUniPosOwner owner) {
|
||||||
|
Cache<Long, IUniPosOwner> cache = dimensionLocks.get(level.dimension());
|
||||||
|
if (cache != null) {
|
||||||
|
// 只有当锁的主人是当前 owner 时,才更新时间
|
||||||
|
// put 操作会重置 expireAfterWrite 的计时器
|
||||||
|
cache.asMap().computeIfPresent(pos.asLong(), (k, v) -> {
|
||||||
|
if (v == owner) return owner;
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user