diff --git a/src/main/java/autostore/client/territory/TerritoryDataService.java b/src/main/java/autostore/client/territory/TerritoryDataService.java index 8c3dc21..17fc798 100644 --- a/src/main/java/autostore/client/territory/TerritoryDataService.java +++ b/src/main/java/autostore/client/territory/TerritoryDataService.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -65,6 +66,16 @@ public final class TerritoryDataService { private volatile long lastSuccessfulFetchMillis; /** 最近一次尝试拉取的时间戳(毫秒) */ private volatile long lastAttemptMillis; + /** 会话内新占领区块的本地记录时间(毫秒),用于刷新时判断该占领是否已被地图数据确认 */ + private final Map sessionClaimTimes = new ConcurrentHashMap<>(); + /** + * 会话占领"确认宽限"时长(毫秒)。 + * + *

占领成功后,服务器动态地图(Dynmap/markers.json)可能尚未同步该区块, + * 因此在宽限期内即使地图未确认也先保留"会话占领"标记,避免刚占领的区块被误判为假占领。 + * 超过宽限期仍未在地图中确认的,视为未真正占领并丢弃,使空洞区块能重新进入规划。 + */ + private static final long CLAIM_CONFIRM_GRACE_MS = 120_000L; public TerritoryDataService() { this.executor = TerritoryDataService.newExecutor(); @@ -111,8 +122,8 @@ public final class TerritoryDataService { /** * 异步刷新领土数据(在调度线程中执行)。 - * 拉取成功后解析 JSON,并把上一份模型中的"会话新占领区块"合并进新模型, - * 避免刷新覆盖掉本次会话的占领记录。 + * 拉取成功后解析 JSON,并把上一份模型中的"会话新占领区块"与刚拉取的最新地图数据仔细核对, + * 只保留"已被地图确认"或"刚占领未超宽限期"的占领记录,避免刷新时把假占领永久留在模型里。 */ public void refreshAsync() { this.executor.execute(() -> { @@ -127,8 +138,25 @@ public final class TerritoryDataService { TerritoryModel parsed = TerritoryDataService.parse(response.body()); TerritoryModel previous = this.model; if (previous != null) { + long now = System.currentTimeMillis(); + // 本次拉取到的己方领土区块(地图数据即最终依据,无需缓存旧数据) + Set freshOwn = new HashSet<>(); + TerritoryState freshSelf = parsed.state(this.selectedStateName); + if (freshSelf != null) { + freshOwn.addAll(freshSelf.chunks()); + } for (ChunkPos sessionClaim : previous.sessionClaims()) { - parsed.addSessionClaim(sessionClaim); + 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); + } else if (claimTime != null) { + // 地图始终未确认且超时:判定为假占领并移除,使空洞区块重新进入规划 + this.sessionClaimTimes.remove(sessionClaim); + } + // claimTime 为 null(从未在本服务记录)也视为未确认:不保留 } } parsed.setSelfStateName(this.selectedStateName); @@ -163,6 +191,7 @@ public final class TerritoryDataService { } public void addSessionClaim(ChunkPos chunk) { + this.sessionClaimTimes.put(chunk, System.currentTimeMillis()); TerritoryModel current = this.model; if (current != null) { current.addSessionClaim(chunk);