v1.2.1(重发): 修复刷新误标已占领(缓存击穿+宽限期60s+防误判)、修复GUI/刷新卡顿与内存(视口过滤/后台预计算/零复制校验/内容去重/ChunkPos驻留)、区域扩地重写为严格S形由内而外(起始边离王城最近)
这个提交包含在:
@@ -451,22 +451,27 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
||||
/**
|
||||
* 判断区块是否与己方领土(己方邦国地图区块 + 本次会话新占领区块)接壤。
|
||||
*
|
||||
* <p>接壤采用四个正方向(上下左右);只对“己方”领土判断,绝不把其他邦国领土计入。</p>
|
||||
* <p>接壤采用四个正方向(上下左右);只对“己方”领土判断,绝不把其他邦国领土计入。
|
||||
* 实现上直接查询四个邻居是否命中集合,零集合复制——
|
||||
* 旧实现每帧把数千个己方区块复制进临时 HashSet,是持续卡顿与内存压力的来源之一。</p>
|
||||
*/
|
||||
private boolean isAdjacentToOwn(ChunkPos chunk) {
|
||||
TerritoryModel model = this.dataService.model();
|
||||
Set<ChunkPos> own = new HashSet<ChunkPos>();
|
||||
TerritoryState self = model.selfState();
|
||||
if (self != null) {
|
||||
own.addAll(self.chunks());
|
||||
}
|
||||
own.addAll(model.sessionClaims());
|
||||
for (ChunkPos neighbor : this.cardinalNeighbors(chunk)) {
|
||||
if (own.contains(neighbor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
Set<ChunkPos> ownChunks = self == null ? null : self.chunks();
|
||||
Set<ChunkPos> sessionClaims = model.sessionClaims();
|
||||
int x = chunk.x();
|
||||
int z = chunk.z();
|
||||
return this.isOwnAt(ownChunks, sessionClaims, x + 1, z)
|
||||
|| this.isOwnAt(ownChunks, sessionClaims, x - 1, z)
|
||||
|| this.isOwnAt(ownChunks, sessionClaims, x, z + 1)
|
||||
|| this.isOwnAt(ownChunks, sessionClaims, x, z - 1);
|
||||
}
|
||||
|
||||
/** 判断指定坐标的区块是否属于己方领土或本次会话新占领 */
|
||||
private boolean isOwnAt(Set<ChunkPos> ownChunks, Set<ChunkPos> sessionClaims, int x, int z) {
|
||||
ChunkPos probe = new ChunkPos(x, z);
|
||||
return ownChunks != null && ownChunks.contains(probe) || sessionClaims.contains(probe);
|
||||
}
|
||||
|
||||
/** 成功后的过渡:进入 NEXT 状态,准备处理下一个目标 */
|
||||
@@ -583,6 +588,9 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
||||
}
|
||||
this.model.addSessionClaim(chunk);
|
||||
this.dataService.addSessionClaim(chunk);
|
||||
// 占领成功后立即触发一次地图刷新(请求侧已带缓存击穿),尽快让地图数据确认该区块,
|
||||
// 缩短"会话占领等待确认"窗口,避免假占领长时间残留
|
||||
this.dataService.refreshAsync();
|
||||
++this.completedClaims;
|
||||
this.target = chunk;
|
||||
this.lastProgress = 100;
|
||||
@@ -599,12 +607,20 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
||||
if (this.target == null) {
|
||||
return;
|
||||
}
|
||||
// 开垦只能发生在玩家所在区块:校验玩家确实位于目标区块上。
|
||||
// 通用成功文案采用模糊匹配,可能被服务器广播的其他玩家开垦消息误触发;
|
||||
// 不校验会把未占领的目标错误标记为"已占领"并跳过,直到很久之后才被地图数据纠正(v1.2.1 修复)。
|
||||
if (!this.playerChunk.equals(this.target)) {
|
||||
return;
|
||||
}
|
||||
// 去重:同一区块只计一次
|
||||
if (!this.claimedSet.add(this.target)) {
|
||||
return;
|
||||
}
|
||||
this.model.addSessionClaim(this.target);
|
||||
this.dataService.addSessionClaim(this.target);
|
||||
// 占领成功后立即触发一次地图刷新,尽快确认该区块
|
||||
this.dataService.refreshAsync();
|
||||
++this.completedClaims;
|
||||
this.lastProgress = 100;
|
||||
this.log("\u5360\u9886\u6210\u529f " + this.target.x() + "," + this.target.z());
|
||||
@@ -622,11 +638,6 @@ implements SubtitleChatMonitor.Listener, BaritoneBridge.PathListener {
|
||||
return this.dataService.model().allClaimedChunks().contains(chunk);
|
||||
}
|
||||
|
||||
/** 返回区块的四个正方向邻居 */
|
||||
private List<ChunkPos> cardinalNeighbors(ChunkPos chunk) {
|
||||
return List.of(new ChunkPos(chunk.x() + 1, chunk.z()), new ChunkPos(chunk.x() - 1, chunk.z()), new ChunkPos(chunk.x(), chunk.z() + 1), new ChunkPos(chunk.x(), chunk.z() - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动游泳逻辑(每帧调用):
|
||||
* 仅在玩家完全没入水中(有溺水风险)时自动跳跃,保持浮在水面。
|
||||
|
||||
在新工单中引用
屏蔽一个用户