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)对在同一时间只能被一个对象 "拥有"。
* 一次只能被一个对象 "拥有"。
*
*
重要:由于使用了 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 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) -> 每维度的坐标缓存。
// 缓存键为 BlockPos 的 long 值,值为 IUniPosOwner 的弱值(weak values)。
// 使用维度键确保:同一维度的不同 Level 实例在服务端共享同一套锁;
// 使用弱值确保:当所有者被 GC 时,锁能自动释放。
private final ConcurrentMap, Cache> dimensionLocks;
private UniPosManager() {
this.dimensionLocks = new ConcurrentHashMap<>();
}
/**
获取或创建特定维度键对应的 Cache。
Cache 使用 BlockPos 的 long 值作为键,IUniPosOwner 的弱值作为值。
按 ResourceKey 分隔缓存,因此同一维度的不同 Level 实例在服务端共享同一套锁。
computeIfAbsent 是原子操作,保证线程安全地获取或创建 Cache。
*/
private Cache 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 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 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;
});
}
}
}