Skip to content

Commit 698aaf0

Browse files
修复bypass,新增随机模式,bossbar指示条
1 parent a252db2 commit 698aaf0

6 files changed

Lines changed: 156 additions & 18 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>ict.minesunshineone</groupId>
88
<artifactId>peek</artifactId>
9-
<version>3.1</version>
9+
<version>3.2</version>
1010

1111
<properties>
1212
<maven.compiler.release>17</maven.compiler.release>

src/main/java/ict/minesunshineone/peek/command/PeekCommand.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.ArrayList;
44
import java.util.Collections;
55
import java.util.List;
6+
import java.util.Random;
67
import java.util.stream.Collectors;
78

89
import org.bukkit.command.Command;
@@ -51,6 +52,8 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
5152
handleDeny(player);
5253
case "self" ->
5354
handleSelfPeek(player);
55+
case "random", "r" ->
56+
handleRandomPeek(player);
5457
default ->
5558
handlePeek(player, args[0]);
5659
};
@@ -132,6 +135,51 @@ private boolean handleSelfPeek(Player player) {
132135
return true;
133136
}
134137

138+
private boolean handleRandomPeek(Player player) {
139+
// 检查玩家是否已经在peek状态
140+
if (plugin.getStateHandler().getActivePeeks().containsKey(player.getUniqueId())) {
141+
plugin.getMessages().send(player, "already-peeking");
142+
return true;
143+
}
144+
145+
// 检查玩家是否死亡
146+
if (player.isDead()) {
147+
plugin.getMessages().send(player, "cannot-peek-while-dead");
148+
return true;
149+
}
150+
151+
// 检查冷却时间
152+
if (plugin.getCooldownManager().isOnCooldown(player)) {
153+
plugin.getMessages().send(player, "cooldown-peek", "time",
154+
String.valueOf(plugin.getCooldownManager().getRemainingCooldown(player)));
155+
return true;
156+
}
157+
158+
// 获取所有可用的目标玩家(排除自己、私密模式玩家、死亡玩家)
159+
// 注意:允许 peek 正在 peek 别人的玩家
160+
List<Player> availableTargets = plugin.getServer().getOnlinePlayers().stream()
161+
.filter(p -> !p.equals(player)) // 排除自己
162+
.filter(p -> !plugin.getPrivacyManager().isPrivateMode(p)) // 排除私密模式玩家
163+
.filter(p -> !p.isDead()) // 排除死亡玩家
164+
.collect(Collectors.toList());
165+
166+
if (availableTargets.isEmpty()) {
167+
plugin.getMessages().send(player, "no-available-random-target");
168+
return true;
169+
}
170+
171+
// 随机选择一个目标
172+
Random random = new Random();
173+
Player target = availableTargets.get(random.nextInt(availableTargets.size()));
174+
175+
// 发送随机选择提示
176+
plugin.getMessages().send(player, "random-peek-selected", "player", target.getName());
177+
178+
// 开始 peek
179+
plugin.getStateHandler().startPeek(player, target);
180+
return true;
181+
}
182+
135183
@Override
136184
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
137185
if (!(sender instanceof Player)) {
@@ -146,6 +194,8 @@ public List<String> onTabComplete(CommandSender sender, Command command, String
146194
completions.add("accept");
147195
completions.add("deny");
148196
completions.add("self");
197+
completions.add("random");
198+
completions.add("r");
149199

150200
if (sender.hasPermission("peek.use")) {
151201
plugin.getServer().getOnlinePlayers().stream()

src/main/java/ict/minesunshineone/peek/handler/PeekStateHandler.java

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
import java.util.Map;
66
import java.util.UUID;
77

8+
import org.bukkit.Bukkit;
89
import org.bukkit.GameMode;
910
import org.bukkit.Location;
1011
import org.bukkit.Sound;
1112
import org.bukkit.attribute.Attribute;
1213
import org.bukkit.attribute.AttributeInstance;
14+
import org.bukkit.boss.BarColor;
15+
import org.bukkit.boss.BarStyle;
16+
import org.bukkit.boss.BossBar;
1317
import org.bukkit.entity.Player;
1418
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
1519
import org.bukkit.util.Vector;
@@ -23,6 +27,7 @@ public class PeekStateHandler {
2327
private final PeekPlugin plugin;
2428
private final Map<UUID, PeekData> activePeeks = new HashMap<>();
2529
private final Map<UUID, ScheduledTask> rangeCheckers = new HashMap<>();
30+
private final Map<UUID, BossBar> distanceBossBars = new HashMap<>();
2631
private final Object rangeCheckersLock = new Object();
2732
private final double maxPeekDistance;
2833

@@ -69,14 +74,19 @@ public void startPeek(Player peeker, Player target) {
6974
teleportAndSetGameMode(peeker, target);
7075
}
7176

72-
// 发送消息
73-
plugin.getMessages().send(peeker, "peek-start", "player", target.getName());
74-
plugin.getMessages().send(target, "being-peeked", "player", peeker.getName());
75-
76-
// 播放声音
77-
playSound(target, "start-peek");
77+
// 检查是否静默 peek(有 bypass 权限)
78+
boolean silentPeek = plugin.getTargetHandler().shouldSilentPeek(peeker);
7879

79-
updateActionBar(target);
80+
// 发送消息给观察者
81+
plugin.getMessages().send(peeker, "peek-start", "player", target.getName());
82+
83+
// 只有非静默模式才通知目标
84+
if (!silentPeek) {
85+
plugin.getMessages().send(target, "being-peeked", "player", peeker.getName());
86+
// 播放声音
87+
playSound(target, "start-peek");
88+
updateActionBar(target);
89+
}
8090
}
8191

8292
public void startSelfPeek(Player peeker) {
@@ -223,18 +233,20 @@ public void endPeek(Player peeker, boolean shouldRestore) {
223233

224234
// 检查是否是自我观察模式(target是自己)
225235
boolean isSelfPeek = target != null && target.getUniqueId().equals(peeker.getUniqueId());
236+
// 检查是否静默 peek(有 bypass 权限)
237+
boolean silentPeek = peeker.isOnline() && plugin.getTargetHandler().shouldSilentPeek(peeker);
226238

227-
// 只有在非自我观察模式下才给目标发送消息
228-
if (target != null && target.isOnline() && !isSelfPeek) {
239+
// 只有在非自我观察模式且非静默模式下才给目标发送消息
240+
if (target != null && target.isOnline() && !isSelfPeek && !silentPeek) {
229241
plugin.getMessages().send(target, "peek-end-target", "player", peeker.getName());
230242
playSound(target, "end-peek");
231243
}
232244

233245
// 最后才移除活动peek
234246
activePeeks.remove(peeker.getUniqueId());
235247

236-
// 更新目标玩家的actionbar(自我观察时不需要更新)
237-
if (target != null && target.isOnline() && !isSelfPeek) {
248+
// 更新目标玩家的actionbar(自我观察时不需要更新,静默模式也不更新
249+
if (target != null && target.isOnline() && !isSelfPeek && !silentPeek) {
238250
updateActionBar(target);
239251
}
240252
}
@@ -271,7 +283,11 @@ private void teleportAndSetGameMode(Player peeker, Player target) {
271283
plugin.getMessages().send(peeker, "teleport-failed");
272284
endPeek(peeker);
273285
} else {
274-
startRangeChecker(peeker, target);
286+
// 传送成功后,使用玩家的实体调度器确保在正确线程执行
287+
peeker.getScheduler().run(plugin, scheduledTask -> {
288+
startRangeChecker(peeker, target);
289+
createDistanceBossBar(peeker, target);
290+
}, null);
275291
}
276292
});
277293
}, 2L); // 2 tick 延迟
@@ -432,6 +448,7 @@ private void startRangeChecker(Player peeker, Player target) {
432448

433449
if (peeker.getWorld().equals(target.getWorld())) {
434450
double distance = peeker.getLocation().distance(target.getLocation());
451+
updateDistanceBossBar(peeker, distance);
435452
if (distance > maxPeekDistance) {
436453
plugin.getMessages().send(peeker, "range-exceeded");
437454
endPeek(peeker);
@@ -459,6 +476,12 @@ public void stopRangeChecker(Player peeker) {
459476
task.cancel();
460477
}
461478
}
479+
// 使用玩家的实体调度器确保在正确的线程上移除 BossBar
480+
if (peeker.isOnline()) {
481+
peeker.getScheduler().run(plugin, scheduledTask -> removeDistanceBossBar(peeker), null);
482+
} else {
483+
removeDistanceBossBar(peeker);
484+
}
462485
}
463486

464487
public void cleanup() {
@@ -468,6 +491,58 @@ public void cleanup() {
468491
rangeCheckers.remove(peeker);
469492
});
470493
}
494+
// 清理所有 BossBar
495+
new HashMap<>(distanceBossBars).forEach((uuid, bar) -> {
496+
bar.removeAll();
497+
distanceBossBars.remove(uuid);
498+
});
499+
}
500+
501+
private void createDistanceBossBar(Player peeker, Player target) {
502+
removeDistanceBossBar(peeker); // 确保没有残留
503+
BossBar bar = Bukkit.createBossBar(
504+
String.format("§d距离 §f%s§d: §e0.0 §7/ §e%.1f §d格", target.getName(), maxPeekDistance),
505+
BarColor.PINK,
506+
BarStyle.SEGMENTED_10
507+
);
508+
bar.setProgress(0.0);
509+
bar.addPlayer(peeker);
510+
distanceBossBars.put(peeker.getUniqueId(), bar);
511+
logDebug("Created BossBar for player %s, target: %s", peeker.getName(), target.getName());
512+
}
513+
514+
private void updateDistanceBossBar(Player peeker, double distance) {
515+
BossBar bar = distanceBossBars.get(peeker.getUniqueId());
516+
if (bar == null) return;
517+
518+
PeekData data = activePeeks.get(peeker.getUniqueId());
519+
if (data == null) return;
520+
521+
Player target = plugin.getServer().getPlayer(data.getTargetUUID());
522+
String targetName = target != null ? target.getName() : "未知";
523+
524+
double progress = Math.min(distance / maxPeekDistance, 1.0);
525+
bar.setProgress(progress);
526+
527+
// 根据距离比例改变颜色
528+
if (progress < 0.5) {
529+
bar.setColor(BarColor.GREEN);
530+
} else if (progress < 0.75) {
531+
bar.setColor(BarColor.YELLOW);
532+
} else if (progress < 0.9) {
533+
bar.setColor(BarColor.RED);
534+
} else {
535+
bar.setColor(BarColor.RED);
536+
}
537+
538+
bar.setTitle(String.format("§d距离 §f%s§d: §e%.1f §7/ §e%.1f §d格", targetName, distance, maxPeekDistance));
539+
}
540+
541+
private void removeDistanceBossBar(Player peeker) {
542+
BossBar bar = distanceBossBars.remove(peeker.getUniqueId());
543+
if (bar != null) {
544+
bar.removeAll();
545+
}
471546
}
472547

473548
public void removeActivePeek(Player player) {

src/main/java/ict/minesunshineone/peek/handler/PeekTargetHandler.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,20 @@ public boolean canPeek(Player peeker, String targetName) {
3636
return false;
3737
}
3838

39-
if (plugin.getPrivacyManager().isPrivateMode(target)) {
39+
// 检查隐私模式,但有 bypass 权限的玩家可以无视
40+
if (plugin.getPrivacyManager().isPrivateMode(target) && !peeker.hasPermission("peek.bypass")) {
4041
plugin.getPrivacyManager().sendPeekRequest(peeker, target);
4142
return false;
4243
}
4344

4445
return true;
4546
}
47+
48+
/**
49+
* 检查是否应该静默 peek(不通知目标)
50+
* 用于有 bypass 权限的玩家
51+
*/
52+
public boolean shouldSilentPeek(Player peeker) {
53+
return peeker.hasPermission("peek.bypass");
54+
}
4655
}

src/main/resources/lang/en_US.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ messages:
44
prefix: ""
55
command-player-only: " &dThis feature can only be used by players!"
66
no-permission: " &dSorry, but you don't have permission to do this!"
7-
usage: " &dGuidance: \n&d➤ &f/peek <player> &7Peek player\n&d➤ &f/peek self &7Enable spectator mode in place\n&d➤ &f/peek exit &7Exit Peek Mode\n&d➤ &f/peek stats &7Get the statistics\n&d➤ &f/peek privacy &7Turn on/off Privacy Mode\n&d➤ &f/peek accept &7Accept Peek request(s)\n&d➤ &f/peek deny &7Deny Peek request(s)"
7+
usage: " &dGuidance: \n&d➤ &f/peek <player> &7Peek player\n&d➤ &f/peek random &7Randomly peek a player\n&d➤ &f/peek self &7Enable spectator mode in place\n&d➤ &f/peek exit &7Exit Peek Mode\n&d➤ &f/peek stats &7Get the statistics\n&d➤ &f/peek privacy &7Turn on/off Privacy Mode\n&d➤ &f/peek accept &7Accept Peek request(s)\n&d➤ &f/peek deny &7Deny Peek request(s)"
88
already-peeking: " &dYou're already peeking someone else,Type &f/peek exit &dto exit."
99
player-not-found: " &dCan't find the player &f{player} &d, Is there a typo in the name?"
1010
cannot-peek-self: " &dYou can't peek yourself!"
@@ -47,4 +47,6 @@ messages:
4747
peek-end-respawn: "&aYou have respawned, your peek state has been restored~"
4848
self-peek-start: " &d✨ Self-peek mode activated! You can now explore freely, remember to use &f/peek exit &dto exit spectator mode~"
4949
self-peek-range-exceeded: " &dYou are too far from your original position, self-peek mode has been automatically ended~"
50-
self-peek-world-changed: " &dDetected world change, self-peek mode has been automatically ended~"
50+
self-peek-world-changed: " &dDetected world change, self-peek mode has been automatically ended~"
51+
random-peek-selected: " &d✨ Randomly selected &f{player}&d, starting peek~"
52+
no-available-random-target: " &dNo available players to randomly peek, everyone is in privacy mode or offline~"

src/main/resources/lang/zh_CN.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ messages:
22
prefix: ""
33
command-player-only: " &d这个魔法只能由玩家施展哦~"
44
no-permission: " &d抱歉,你还没有获得使用这个魔法的权限呢~"
5-
usage: " &d施法指南:\n&d➤ &f/peek <玩家名> &7贴贴指定玩家\n&d➤ &f/peek self &7在原地开启观察者模式\n&d➤ &f/peek exit &7退出贴贴模式\n&d➤ &f/peek stats &7查看贴贴统计\n&d➤ &f/peek privacy &7切换私人模式\n&d➤ &f/peek accept &7接受贴贴请求\n&d➤ &f/peek deny &7拒绝贴贴请求"
5+
usage: " &d施法指南:\n&d➤ &f/peek <玩家名> &7贴贴指定玩家\n&d➤ &f/peek random &7随机贴贴一个玩家\n&d➤ &f/peek self &7在原地开启观察者模式\n&d➤ &f/peek exit &7退出贴贴模式\n&d➤ &f/peek stats &7查看贴贴统计\n&d➤ &f/peek privacy &7切换私人模式\n&d➤ &f/peek accept &7接受贴贴请求\n&d➤ &f/peek deny &7拒绝贴贴请求"
66
already-peeking: " &d你已经在贴贴别人了呢,输入 &f/peek exit &d解除魔法吧~"
77
player-not-found: " &d咦?找不到玩家 &f{player} &d呢,是不是拼错名字啦?"
88
cannot-peek-self: " &d自己贴贴自己?这可不行呢,试试贴贴别人吧!"
@@ -45,4 +45,6 @@ messages:
4545
peek-end-respawn: "&a你已重生,贴贴状态已恢复~"
4646
self-peek-start: " &d✨ 自我观察模式已启动!你现在可以自由探索,记得用 &f/peek exit &d退出观察模式~"
4747
self-peek-range-exceeded: " &d你离原位置太远了,自我观察模式已自动结束~"
48-
self-peek-world-changed: " &d检测到你切换了世界,自我观察模式已自动结束~"
48+
self-peek-world-changed: " &d检测到你切换了世界,自我观察模式已自动结束~"
49+
random-peek-selected: " &d✨ 随机选中了 &f{player}&d,正在施展贴贴魔法~"
50+
no-available-random-target: " &d当前没有可以随机贴贴的玩家呢,大家都在私密模式或者不在线~"

0 commit comments

Comments
 (0)