Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 60 additions & 59 deletions src/main/java/ict/minesunshineone/peek/handler/PeekStateHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ public void startPeek(Player peeker, Player target) {
}

// 防止并发修改
final PeekData data;
synchronized (activePeeks) {
if (activePeeks.containsKey(peeker.getUniqueId())) {
plugin.getMessages().send(peeker, "already-peeking");
return;
}

logDebug("Starting peek: %s -> %s", peeker.getName(), target.getName());
PeekData data = new PeekData(
data = new PeekData(
peeker.getLocation().clone(),
peeker.getGameMode(),
target.getUniqueId(),
Expand All @@ -70,12 +71,13 @@ public void startPeek(Player peeker, Player target) {
peeker.getActivePotionEffects());

activePeeks.put(peeker.getUniqueId(), data);
plugin.getStateManager().savePlayerState(peeker, data);
plugin.getStatisticsManager().recordPeekStart(peeker, target);

teleportAndSetGameMode(peeker, target);
}

plugin.getStateManager().savePlayerState(peeker, data);
plugin.getStatisticsManager().recordPeekStart(peeker, target);

teleportAndSetGameMode(peeker, target);

// 检查是否静默 peek(有 bypass 权限)
boolean silentPeek = plugin.getTargetHandler().shouldSilentPeek(peeker);

Expand Down Expand Up @@ -246,69 +248,68 @@ public void endPeek(Player peeker) {
// ==================== 私有方法 ====================

private void teleportAndSetGameMode(Player peeker, Player target) {
final Runnable onFailed = () -> {
plugin.getMessages().send(peeker, "teleport-failed");
endPeek(peeker);
};

// 先切换游戏模式
plugin.getServer().getRegionScheduler().run(plugin, peeker.getLocation(), task -> {
try {
// 清理骑乘状态,准备进入旁观者模式
PlayerStateUtil.prepareForSpectatorMode(peeker, plugin.getLogger());

// 设置为旁观模式
peeker.setGameMode(GameMode.SPECTATOR);

// 等待2 tick后再传送
plugin.getServer().getRegionScheduler().runDelayed(plugin, peeker.getLocation(), delayedTask -> {
peeker.teleportAsync(target.getLocation(), TeleportCause.PLUGIN).thenAccept(success -> {
if (!success) {
plugin.getMessages().send(peeker, "teleport-failed");
endPeek(peeker);
} else {
// 传送成功后,使用玩家的实体调度器确保在正确线程执行
peeker.getScheduler().run(plugin, scheduledTask -> {
bossBarHandler.createDistanceBossBar(peeker, target);
startNormalRangeChecker(peeker, target);
}, null);
}
});
}, 2L); // 2 tick 延迟
} catch (Exception e) {
plugin.getLogger().warning(String.format("为玩家 %s 切换游戏模式时发生错误", peeker.getName()));
peeker.setSneaking(false); // 确保异常时也恢复状态
endPeek(peeker);
final boolean firstRoundScheduled = peeker.getScheduler().execute(plugin, () -> {
// 清理骑乘状态,准备进入旁观者模式
PlayerStateUtil.prepareForSpectatorMode(peeker);

// 设置为旁观模式
peeker.setGameMode(GameMode.SPECTATOR);

// 等待2 tick后再传送
final boolean secondRoundScheduled = peeker.getScheduler().execute(plugin, () -> {
peeker.teleportAsync(target.getLocation(), TeleportCause.PLUGIN).thenAccept(success -> {
if (!success) {
onFailed.run();
} else {
// 传送成功后,使用玩家的实体调度器确保在正确线程执行
bossBarHandler.createDistanceBossBar(peeker, target);
startNormalRangeChecker(peeker, target);
}
});
}, onFailed, 2L); // 2 tick 延迟

if (!secondRoundScheduled) {
onFailed.run();
}
});
}, onFailed, 1L);

if (!firstRoundScheduled) {
onFailed.run();
}
}

private void setSelfPeekGameMode(Player peeker) {
plugin.getServer().getRegionScheduler().run(plugin, peeker.getLocation(), task -> {
try {
if (!peeker.isOnline()) {
plugin.getLogger().warning(String.format("玩家 %s 在设置自我观察模式时已离线", peeker.getName()));
endPeek(peeker, false);
return;
}
peeker.getScheduler().execute(plugin, () -> {
// 这里如果玩家离线了调度器会自动退役
/*if (!peeker.isOnline()) {
plugin.getLogger().warning(String.format("玩家 %s 在设置自我观察模式时已离线", peeker.getName()));
endPeek(peeker, false);
return;
}*/

if (peeker.isDead()) {
plugin.getLogger().warning(String.format("玩家 %s 在设置自我观察模式时已死亡", peeker.getName()));
plugin.getMessages().send(peeker, "cannot-peek-while-dead");
endPeek(peeker, false);
return;
}
if (peeker.isDead()) {
plugin.getSLF4JLogger().warn("玩家 {} 在设置自我观察模式时已死亡", peeker.getName());
plugin.getMessages().send(peeker, "cannot-peek-while-dead");
endPeek(peeker, false);
return;
}

// 清理骑乘状态,准备进入旁观者模式
PlayerStateUtil.prepareForSpectatorMode(peeker, plugin.getLogger());
// 清理骑乘状态,准备进入旁观者模式
PlayerStateUtil.prepareForSpectatorMode(peeker);

peeker.setGameMode(GameMode.SPECTATOR);
peeker.setGameMode(GameMode.SPECTATOR);

logDebug("Successfully set self peek game mode for player: %s", peeker.getName());
} catch (Exception e) {
plugin.getLogger().warning(String.format("为玩家 %s 设置自我观察模式时发生错误: %s", peeker.getName(), e.getMessage()));
if (plugin.getConfig().getBoolean("debug", false)) {
e.printStackTrace();
}
peeker.setSneaking(false); // 确保异常时也恢复状态
endPeek(peeker);
}
});
logDebug("Successfully set self peek game mode for player: %s", peeker.getName());
}, () -> {
plugin.getSLF4JLogger().warn("玩家 {} 在设置自我观察模式时已离线", peeker.getName());
endPeek(peeker, false);
}, 1L);
}

private void startNormalRangeChecker(Player peeker, Player target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* 包括传送、游戏模式、生命值、饥饿度、药水效果等
*/
public class PlayerStateRestorer {
private static final Vector ZERO_VECTOR = new Vector(0, 0, 0);

private final PeekPlugin plugin;

Expand All @@ -30,25 +31,29 @@ public PlayerStateRestorer(PeekPlugin plugin) {
* @param data PeekData 数据
*/
public void restorePlayerState(Player peeker, PeekData data) {
plugin.getServer().getRegionScheduler().run(plugin, data.getOriginalLocation(), task -> {
final Runnable onFailed = () -> handleTeleportFailure(peeker, data);

final boolean scheduled = peeker.getScheduler().execute(plugin, ()-> {

// 强制清理任何骑乘/附身状态,防止卡在旁观者模式
PlayerStateUtil.forceExitRidingState(peeker, plugin.getLogger());
PlayerStateUtil.forceExitRidingState(peeker);

// 在传送前先清除动量
peeker.setVelocity(new Vector(0, 0, 0));
peeker.setVelocity(ZERO_VECTOR);

peeker.teleportAsync(data.getOriginalLocation(), TeleportCause.PLUGIN).thenAccept(success -> {
if (success) {
// 传送成功后再改变游戏模式
plugin.getServer().getRegionScheduler().run(plugin, data.getOriginalLocation(), modeTask -> {
applyRestoredState(peeker, data);
});
applyRestoredState(peeker, data);
} else {
handleTeleportFailure(peeker, data);
onFailed.run();
}
});
});
}, onFailed, 1L);

if (!scheduled) {
onFailed.run();
}
}

/**
Expand All @@ -62,28 +67,31 @@ public void handleTeleportFailure(Player peeker, PeekData data) {
"无法将玩家 %s 传送回原位置,正在尝试传送到重生点",
peeker.getName()));

Location spawnLoc = resolveSpawnLocation(peeker);
final Location spawnLoc = resolveSpawnLocation(peeker);

final Runnable onFailed = () -> {
plugin.getLogger().severe(String.format(
"未找到可用的重生点,强制恢复玩家 %s 的状态",
peeker.getName()));
forceStateRestoreWithoutTeleport(peeker, data);
};

if (spawnLoc != null) {
plugin.getServer().getRegionScheduler().run(plugin, spawnLoc, spawnTask -> {
final boolean scheduled = peeker.getScheduler().execute(plugin, () -> {
peeker.teleportAsync(spawnLoc, TeleportCause.PLUGIN).thenAccept(spawnSuccess -> {
if (spawnSuccess) {
plugin.getServer().getRegionScheduler().run(plugin, spawnLoc, modeTask -> {
applyRestoredState(peeker, data);
});
applyRestoredState(peeker, data);
} else {
plugin.getLogger().severe(String.format(
"无法将玩家 %s 传送到任何安全位置",
peeker.getName()));
forceStateRestoreWithoutTeleport(peeker, data);
onFailed.run();
}
});
});
}, onFailed, 1L);

if (!scheduled) {
onFailed.run();
}
} else {
plugin.getLogger().severe(String.format(
"未找到可用的重生点,强制恢复玩家 %s 的状态",
peeker.getName()));
forceStateRestoreWithoutTeleport(peeker, data);
onFailed.run();
}

plugin.getMessages().send(peeker, "teleport-failed");
Expand All @@ -97,17 +105,13 @@ public void handleTeleportFailure(Player peeker, PeekData data) {
*/
public Location resolveSpawnLocation(Player peeker) {
Location respawnLocation = peeker.getRespawnLocation();

if (respawnLocation != null) {
return respawnLocation;
}

if (peeker.getWorld() != null) {
return peeker.getWorld().getSpawnLocation();
}
return peeker.getWorld().getSpawnLocation();

return plugin.getServer().getWorlds().isEmpty()
? null
: plugin.getServer().getWorlds().get(0).getSpawnLocation();
}

/**
Expand All @@ -117,9 +121,7 @@ public Location resolveSpawnLocation(Player peeker) {
* @param data PeekData 数据
*/
public void forceStateRestoreWithoutTeleport(Player peeker, PeekData data) {
plugin.getServer().getRegionScheduler().run(plugin, peeker.getLocation(), modeTask -> {
applyRestoredState(peeker, data);
});
applyRestoredState(peeker, data);
}

/**
Expand All @@ -133,7 +135,7 @@ public void applyRestoredState(Player peeker, PeekData data) {
peeker.setVelocity(new Vector(0, 0, 0));

// 再次强制清理骑乘/附身状态,确保万无一失
PlayerStateUtil.forceExitRidingState(peeker, plugin.getLogger());
PlayerStateUtil.forceExitRidingState(peeker);

peeker.setGameMode(data.getOriginalGameMode());
double maxHealth = getMaxHealth(peeker);
Expand Down
Loading