v1.1.8: 刷新地图时核对已占领区块,丢弃地图未确认的超期假占领,修复高地空洞

这个提交包含在:
lyizai
2026-08-18 18:59:59 +08:00
父节点 1f817199f7
当前提交 d2a52199ae
@@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -65,6 +66,16 @@ public final class TerritoryDataService {
private volatile long lastSuccessfulFetchMillis; private volatile long lastSuccessfulFetchMillis;
/** 最近一次尝试拉取的时间戳(毫秒) */ /** 最近一次尝试拉取的时间戳(毫秒) */
private volatile long lastAttemptMillis; private volatile long lastAttemptMillis;
/** 会话内新占领区块的本地记录时间(毫秒),用于刷新时判断该占领是否已被地图数据确认 */
private final Map<ChunkPos, Long> sessionClaimTimes = new ConcurrentHashMap<>();
/**
* 会话占领"确认宽限"时长(毫秒)。
*
* <p>占领成功后,服务器动态地图(Dynmap/markers.json)可能尚未同步该区块,
* 因此在宽限期内即使地图未确认也先保留"会话占领"标记,避免刚占领的区块被误判为假占领。
* 超过宽限期仍未在地图中确认的,视为未真正占领并丢弃,使空洞区块能重新进入规划。
*/
private static final long CLAIM_CONFIRM_GRACE_MS = 120_000L;
public TerritoryDataService() { public TerritoryDataService() {
this.executor = TerritoryDataService.newExecutor(); this.executor = TerritoryDataService.newExecutor();
@@ -111,8 +122,8 @@ public final class TerritoryDataService {
/** /**
* 异步刷新领土数据(在调度线程中执行)。 * 异步刷新领土数据(在调度线程中执行)。
* 拉取成功后解析 JSON,并把上一份模型中的"会话新占领区块"合并进新模型, * 拉取成功后解析 JSON,并把上一份模型中的"会话新占领区块"与刚拉取的最新地图数据仔细核对,
* 避免刷新覆盖掉本次会话的占领记录。 * 只保留"已被地图确认"或"刚占领未超宽限期"的占领记录,避免刷新时把假占领永久留在模型里。
*/ */
public void refreshAsync() { public void refreshAsync() {
this.executor.execute(() -> { this.executor.execute(() -> {
@@ -127,8 +138,25 @@ public final class TerritoryDataService {
TerritoryModel parsed = TerritoryDataService.parse(response.body()); TerritoryModel parsed = TerritoryDataService.parse(response.body());
TerritoryModel previous = this.model; TerritoryModel previous = this.model;
if (previous != null) { if (previous != null) {
long now = System.currentTimeMillis();
// 本次拉取到的己方领土区块(地图数据即最终依据,无需缓存旧数据)
Set<ChunkPos> freshOwn = new HashSet<>();
TerritoryState freshSelf = parsed.state(this.selectedStateName);
if (freshSelf != null) {
freshOwn.addAll(freshSelf.chunks());
}
for (ChunkPos sessionClaim : previous.sessionClaims()) { for (ChunkPos sessionClaim : previous.sessionClaims()) {
Long claimTime = this.sessionClaimTimes.get(sessionClaim);
boolean confirmed = freshOwn.contains(sessionClaim);
boolean withinGrace = claimTime != null && now - claimTime < CLAIM_CONFIRM_GRACE_MS;
if (confirmed || withinGrace) {
// 地图已确认,或尚在宽限期(Dynmap 可能还没同步):保留会话占领标记
parsed.addSessionClaim(sessionClaim); parsed.addSessionClaim(sessionClaim);
} else if (claimTime != null) {
// 地图始终未确认且超时:判定为假占领并移除,使空洞区块重新进入规划
this.sessionClaimTimes.remove(sessionClaim);
}
// claimTime 为 null(从未在本服务记录)也视为未确认:不保留
} }
} }
parsed.setSelfStateName(this.selectedStateName); parsed.setSelfStateName(this.selectedStateName);
@@ -163,6 +191,7 @@ public final class TerritoryDataService {
} }
public void addSessionClaim(ChunkPos chunk) { public void addSessionClaim(ChunkPos chunk) {
this.sessionClaimTimes.put(chunk, System.currentTimeMillis());
TerritoryModel current = this.model; TerritoryModel current = this.model;
if (current != null) { if (current != null) {
current.addSessionClaim(chunk); current.addSessionClaim(chunk);