@@ -76,6 +76,83 @@ public void startPeek(Player peeker, Player target) {
7676 updateActionBar (target );
7777 }
7878
79+ public void startSelfPeek (Player peeker ) {
80+ if (peeker == null ) {
81+ plugin .getLogger ().warning ("尝试对空玩家使用自我观察功能" );
82+ return ;
83+ }
84+
85+ // 添加死亡检查
86+ if (peeker .isDead ()) {
87+ plugin .getMessages ().send (peeker , "cannot-peek-while-dead" );
88+ return ;
89+ }
90+
91+ // 添加在线检查
92+ if (!peeker .isOnline ()) {
93+ plugin .getLogger ().warning (String .format ("尝试对离线玩家 %s 使用自我观察功能" , peeker .getName ()));
94+ return ;
95+ }
96+
97+ // 防止并发修改
98+ synchronized (activePeeks ) {
99+ if (activePeeks .containsKey (peeker .getUniqueId ())) {
100+ plugin .getMessages ().send (peeker , "already-peeking" );
101+ return ;
102+ }
103+
104+ try {
105+ logDebug ("Starting self peek: %s" , peeker .getName ());
106+
107+ // 保存当前位置作为原始位置
108+ Location originalLocation = peeker .getLocation ().clone ();
109+
110+ // 创建特殊的 PeekData,目标设为自己
111+ PeekData data = new PeekData (
112+ originalLocation ,
113+ peeker .getGameMode (),
114+ peeker .getUniqueId (), // 目标设为自己
115+ System .currentTimeMillis (),
116+ peeker .getHealth (),
117+ peeker .getFoodLevel (),
118+ peeker .getSaturation (),
119+ peeker .getActivePotionEffects ()
120+ );
121+
122+ activePeeks .put (peeker .getUniqueId (), data );
123+ plugin .getStateManager ().savePlayerState (peeker , data );
124+ plugin .getStatisticsManager ().recordPeekStart (peeker , peeker ); // 统计中目标也是自己
125+
126+ // 设置为观察者模式但不传送
127+ setSelfPeekGameMode (peeker );
128+
129+ // 发送消息
130+ plugin .getMessages ().send (peeker , "self-peek-start" );
131+
132+ // 播放声音
133+ playSound (peeker , "start-peek" );
134+
135+ // 启动self距离检查器,确保不会超出距离限制
136+ startSelfRangeChecker (peeker , originalLocation );
137+
138+ logDebug ("Self peek started successfully for player: %s" , peeker .getName ());
139+
140+ } catch (Exception e ) {
141+ plugin .getLogger ().warning (String .format ("启动自我观察时发生错误,玩家: %s,错误: %s" , peeker .getName (), e .getMessage ()));
142+ if (plugin .getConfig ().getBoolean ("debug" , false )) {
143+ e .printStackTrace ();
144+ }
145+
146+ // 清理可能的残留状态
147+ activePeeks .remove (peeker .getUniqueId ());
148+ stopRangeChecker (peeker );
149+
150+ // 通知玩家发生错误
151+ plugin .getMessages ().send (peeker , "command-error" );
152+ }
153+ }
154+ }
155+
79156 public void endPeek (Player peeker , boolean shouldRestore ) {
80157 if (peeker == null ) {
81158 plugin .getLogger ().warning ("尝试对空玩家结束贴贴功能" );
@@ -140,16 +217,21 @@ public void endPeek(Player peeker, boolean shouldRestore) {
140217 if (peeker .isOnline ()) {
141218 plugin .getMessages ().send (peeker , "peek-end" );
142219 }
143- if (target != null && target .isOnline ()) {
220+
221+ // 检查是否是自我观察模式(target是自己)
222+ boolean isSelfPeek = target != null && target .getUniqueId ().equals (peeker .getUniqueId ());
223+
224+ // 只有在非自我观察模式下才给目标发送消息
225+ if (target != null && target .isOnline () && !isSelfPeek ) {
144226 plugin .getMessages ().send (target , "peek-end-target" , "player" , peeker .getName ());
145227 playSound (target , "end-peek" );
146228 }
147229
148230 // 最后才移除活动peek
149231 activePeeks .remove (peeker .getUniqueId ());
150232
151- // 更新目标玩家的actionbar
152- if (target != null && target .isOnline ()) {
233+ // 更新目标玩家的actionbar(自我观察时不需要更新)
234+ if (target != null && target .isOnline () && ! isSelfPeek ) {
153235 updateActionBar (target );
154236 }
155237 }
@@ -168,23 +250,28 @@ private void teleportAndSetGameMode(Player peeker, Player target) {
168250 peeker .wakeup (false );
169251 }
170252
253+ // 传送之前先设置为蹲下
254+ peeker .setSneaking (true );
255+
171256 // 如果玩家在附身状态,先退出附身
172257 if (peeker .getGameMode () == GameMode .SPECTATOR && peeker .getSpectatorTarget () != null ) {
173258 peeker .setSpectatorTarget (null );
174259 }
175260
261+ // 设置为旁观模式
176262 peeker .setGameMode (GameMode .SPECTATOR );
177263
178- // 切换完成后再传送
179- peeker .teleportAsync (target .getLocation ()).thenAccept (success -> {
180- if (!success ) {
181- plugin .getMessages ().send (peeker , "teleport-failed" );
182- endPeek (peeker );
183- } else {
184- // 传送成功后再启动距离检查器
185- startRangeChecker (peeker , target );
186- }
187- });
264+ // 等待1 tick后再传送
265+ plugin .getServer ().getRegionScheduler ().runDelayed (plugin , peeker .getLocation (), delayedTask -> {
266+ peeker .teleportAsync (target .getLocation ()).thenAccept (success -> {
267+ if (!success ) {
268+ plugin .getMessages ().send (peeker , "teleport-failed" );
269+ endPeek (peeker );
270+ } else {
271+ startRangeChecker (peeker , target );
272+ }
273+ });
274+ }, 2L ); // 2 tick 延迟
188275 } catch (Exception e ) {
189276 plugin .getLogger ().warning (String .format ("为玩家 %s 切换游戏模式时发生错误" , peeker .getName ()));
190277 endPeek (peeker );
@@ -353,4 +440,106 @@ public void updateActionBar(Player target) {
353440 "count" , String .valueOf (peekerCount ));
354441 }
355442 }
443+
444+ private void setSelfPeekGameMode (Player peeker ) {
445+ plugin .getServer ().getRegionScheduler ().run (plugin , peeker .getLocation (), task -> {
446+ try {
447+ // 额外的在线检查
448+ if (!peeker .isOnline ()) {
449+ plugin .getLogger ().warning (String .format ("玩家 %s 在设置自我观察模式时已离线" , peeker .getName ()));
450+ endPeek (peeker , false );
451+ return ;
452+ }
453+
454+ // 死亡检查
455+ if (peeker .isDead ()) {
456+ plugin .getLogger ().warning (String .format ("玩家 %s 在设置自我观察模式时已死亡" , peeker .getName ()));
457+ plugin .getMessages ().send (peeker , "cannot-peek-while-dead" );
458+ endPeek (peeker , false );
459+ return ;
460+ }
461+
462+ // 如果玩家在睡觉,先让他离开床
463+ if (peeker .isSleeping ()) {
464+ peeker .wakeup (false );
465+ }
466+
467+ // 如果玩家在附身状态,先退出附身
468+ if (peeker .getGameMode () == GameMode .SPECTATOR && peeker .getSpectatorTarget () != null ) {
469+ peeker .setSpectatorTarget (null );
470+ }
471+
472+ // 设置为旁观模式,但不传送
473+ peeker .setGameMode (GameMode .SPECTATOR );
474+
475+ logDebug ("Successfully set self peek game mode for player: %s" , peeker .getName ());
476+ } catch (Exception e ) {
477+ plugin .getLogger ().warning (String .format ("为玩家 %s 设置自我观察模式时发生错误: %s" , peeker .getName (), e .getMessage ()));
478+ if (plugin .getConfig ().getBoolean ("debug" , false )) {
479+ e .printStackTrace ();
480+ }
481+ endPeek (peeker );
482+ }
483+ });
484+ }
485+
486+ private void startSelfRangeChecker (Player peeker , Location originalLocation ) {
487+ synchronized (rangeCheckersLock ) {
488+ // 先停止已有的检查器(如果有的话)
489+ stopRangeChecker (peeker );
490+
491+ ScheduledTask task = plugin .getServer ().getRegionScheduler ().runAtFixedRate (plugin ,
492+ originalLocation , // 使用原始位置
493+ scheduledTask -> {
494+ // 检查玩家是否在线
495+ if (!peeker .isOnline ()) {
496+ logDebug ("Player %s went offline during self peek, ending peek" , peeker .getName ());
497+ endPeek (peeker );
498+ return ;
499+ }
500+
501+ try {
502+ // 检查观察者是否死亡(与普通peek逻辑一致)
503+ if (peeker .isDead ()) {
504+ logDebug ("Player %s died during self peek, but continuing to monitor for respawn" , peeker .getName ());
505+ return ; // 不立即结束,等待重生处理
506+ }
507+
508+ // 额外的状态检查
509+ PeekData data = activePeeks .get (peeker .getUniqueId ());
510+ if (data == null ) {
511+ logDebug ("PeekData for player %s is null, stopping range checker" , peeker .getName ());
512+ scheduledTask .cancel ();
513+ return ;
514+ }
515+
516+ // 检查是否超出距离限制(相对于原始位置)
517+ if (peeker .getWorld ().equals (originalLocation .getWorld ())) {
518+ double distance = peeker .getLocation ().distance (originalLocation );
519+ if (distance > maxPeekDistance ) {
520+ logDebug ("Player %s exceeded self peek distance: %.2f > %.2f" ,
521+ peeker .getName (), distance , maxPeekDistance );
522+ plugin .getMessages ().send (peeker , "self-peek-range-exceeded" );
523+ endPeek (peeker );
524+ }
525+ } else {
526+ // 如果换了世界,自动结束自我观察
527+ logDebug ("Player %s changed world during self peek" , peeker .getName ());
528+ plugin .getMessages ().send (peeker , "self-peek-world-changed" );
529+ endPeek (peeker );
530+ }
531+ } catch (Exception e ) {
532+ plugin .getLogger ().warning (String .format ("自我观察距离检查时发生错误:%s" , e .getMessage ()));
533+ if (plugin .getConfig ().getBoolean ("debug" , false )) {
534+ e .printStackTrace ();
535+ }
536+ endPeek (peeker );
537+ }
538+ },
539+ 1L , 10L );
540+
541+ rangeCheckers .put (peeker .getUniqueId (), task );
542+ logDebug ("Started self range checker for player: %s" , peeker .getName ());
543+ }
544+ }
356545}
0 commit comments