diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/EternalCoreApi.java b/eternalcore-api/src/main/java/com/eternalcode/core/EternalCoreApi.java index 9c56892ce..e97035080 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/EternalCoreApi.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/EternalCoreApi.java @@ -2,6 +2,7 @@ import com.eternalcode.core.feature.afk.AfkService; import com.eternalcode.core.feature.catboy.CatboyService; +import com.eternalcode.core.feature.helpop.HelpOpService; import com.eternalcode.core.feature.home.HomeService; import com.eternalcode.core.feature.jail.JailService; import com.eternalcode.core.feature.msg.MsgService; @@ -19,6 +20,8 @@ public interface EternalCoreApi { IgnoreService getIgnoreService(); + HelpOpService getHelpOpService(); + HomeService getHomeService(); JailService getJailService(); diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/HelpOpService.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/HelpOpService.java new file mode 100644 index 000000000..6225c73c2 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/HelpOpService.java @@ -0,0 +1,30 @@ +package com.eternalcode.core.feature.helpop; + +import java.util.UUID; +import org.jetbrains.annotations.NotNull; + +/** + * Service responsible for managing helpop functionality. + */ +public interface HelpOpService { + + /** + * Marks a player as having sent a helpop request. + * + *

This allows administrators to reply to the player's request + * using the helpop reply command. + * + * @param playerUuid the UUID of the player who sent the helpop request + */ + void markSender(@NotNull UUID playerUuid); + + /** + * Checks if a player has sent a helpop request. + * + *

Helpop requests expire after a configured duration (default: 1 hour). + * + * @param playerUuid the UUID of the player to check + * @return {@code true} if the player has an active helpop request, {@code false} otherwise + */ + boolean hasSentHelpOp(@NotNull UUID playerUuid); +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/event/HelpOpReplyEvent.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/event/HelpOpReplyEvent.java new file mode 100644 index 000000000..3c0f7fbee --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/helpop/event/HelpOpReplyEvent.java @@ -0,0 +1,92 @@ +package com.eternalcode.core.feature.helpop.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Event called when an administrator replies to a player's helpop request. + * + *

This event is triggered before the reply message is actually sent to the player. + * Plugins can cancel this event to prevent the reply from being sent, or modify + * the reply content before it's delivered. + */ +public class HelpOpReplyEvent extends Event implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Player admin; + private final Player target; + private String content; + private boolean cancelled; + + public HelpOpReplyEvent(@NotNull Player admin, @NotNull Player target, @NotNull String content) { + super(false); + + this.admin = admin; + this.target = target; + this.content = content; + } + + /** + * Gets the administrator who is replying. + * + * @return the admin player + */ + @NotNull + public Player getAdmin() { + return this.admin; + } + + /** + * Gets the player who originally sent the helpop request. + * + * @return the target player + */ + @NotNull + public Player getTarget() { + return this.target; + } + + /** + * Gets the reply message content. + * + * @return the reply content + */ + @NotNull + public String getContent() { + return this.content; + } + + /** + * Sets the reply message content. + * + * @param content the new reply content + */ + public void setContent(@NotNull String content) { + this.content = content; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + @NotNull + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + @NotNull + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/EternalCoreApiImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/EternalCoreApiImpl.java index 4680437ba..4123fcaac 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/EternalCoreApiImpl.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/EternalCoreApiImpl.java @@ -2,6 +2,7 @@ import com.eternalcode.core.feature.afk.AfkService; import com.eternalcode.core.feature.catboy.CatboyService; +import com.eternalcode.core.feature.helpop.HelpOpService; import com.eternalcode.core.feature.home.HomeService; import com.eternalcode.core.feature.ignore.IgnoreService; import com.eternalcode.core.feature.jail.JailService; @@ -35,6 +36,11 @@ public IgnoreService getIgnoreService() { return this.dependencyProvider.getDependency(IgnoreService.class); } + @Override + public HelpOpService getHelpOpService() { + return this.dependencyProvider.getDependency(HelpOpService.class); + } + @Override public HomeService getHomeService() { return this.dependencyProvider.getDependency(HomeService.class); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java index 5889582c5..02d429771 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java @@ -33,14 +33,22 @@ class HelpOpCommand { private final NoticeService noticeService; private final HelpOpSettings helpOpSettings; + private final HelpOpService helpOpService; private final EventCaller eventCaller; private final Server server; private final Delay delay; @Inject - HelpOpCommand(NoticeService noticeService, HelpOpSettings helpOpSettings, EventCaller eventCaller, Server server) { + HelpOpCommand( + NoticeService noticeService, + HelpOpSettings helpOpSettings, + HelpOpService helpOpService, + EventCaller eventCaller, + Server server + ) { this.noticeService = noticeService; this.helpOpSettings = helpOpSettings; + this.helpOpService = helpOpService; this.eventCaller = eventCaller; this.server = server; this.delay = Delay.withDefault(() -> this.helpOpSettings.helpOpDelay()); @@ -49,7 +57,7 @@ class HelpOpCommand { @Execute @DescriptionDocs(description = "Send helpop message to all administrator with eternalcore.helpop.spy permission", arguments = "") void execute(@Sender Player player, @Join String message) { - UUID uuid = player.getUniqueId(); + UUID sender = player.getUniqueId(); HelpOpEvent event = new HelpOpEvent(player, message); this.eventCaller.callEvent(event); @@ -58,33 +66,25 @@ void execute(@Sender Player player, @Join String message) { return; } - if (this.delay.hasDelay(uuid)) { - Duration time = this.delay.getRemaining(uuid); + if (this.delay.hasDelay(sender)) { + Duration time = this.delay.getRemaining(sender); this.noticeService.create() .notice(translation -> translation.helpOp().helpOpDelay()) .placeholder("{TIME}", DurationUtil.format(time, true)) - .player(uuid) + .player(sender) .send(); return; } - NoticeBroadcast notice = this.noticeService.create() + this.noticeService.create() .console() .notice(translation -> translation.helpOp().format()) + .onlinePlayers(HELPOP_SPY) .placeholder("{PLAYER}", player.getName()) - .placeholder("{TEXT}", MiniMessage.miniMessage().escapeTags(message)); - - for (Player admin : this.server.getOnlinePlayers()) { - if (!admin.hasPermission(HELPOP_SPY)) { - continue; - } - - notice = notice.player(admin.getUniqueId()); - } - - notice.send(); + .placeholder("{TEXT}", MiniMessage.miniMessage().escapeTags(message)) + .send(); this.noticeService .create() @@ -92,6 +92,7 @@ void execute(@Sender Player player, @Join String message) { .notice(translation -> translation.helpOp().send()) .send(); - this.delay.markDelay(uuid); + this.delay.markDelay(sender); + this.helpOpService.markSender(sender); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpReplyCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpReplyCommand.java new file mode 100644 index 000000000..d5e69e081 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpReplyCommand.java @@ -0,0 +1,76 @@ +package com.eternalcode.core.feature.helpop; + +import com.eternalcode.annotations.scan.command.DescriptionDocs; +import com.eternalcode.annotations.scan.permission.PermissionDocs; +import com.eternalcode.core.event.EventCaller; +import com.eternalcode.core.feature.helpop.event.HelpOpReplyEvent; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.notice.NoticeService; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Sender; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.join.Join; +import dev.rollczi.litecommands.annotations.permission.Permission; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.entity.Player; + +@Command(name = "helpopreply") +@Permission("eternalcore.helpop.reply") +@PermissionDocs( + name = "HelpOp Reply", + description = "Allows administrator to reply to a player's helpop request", + permission = "eternalcore.helpop.reply" +) +class HelpOpReplyCommand { + + private final NoticeService noticeService; + private final HelpOpService helpOpService; + private final EventCaller eventCaller; + + @Inject + HelpOpReplyCommand(NoticeService noticeService, HelpOpService helpOpService, EventCaller eventCaller) { + this.noticeService = noticeService; + this.helpOpService = helpOpService; + this.eventCaller = eventCaller; + } + + @Execute + @DescriptionDocs(description = "Reply to a player's helpop request", arguments = " ") + void execute(@Sender Player admin, @Arg Player target, @Join String message) { + if (!this.helpOpService.hasSentHelpOp(target.getUniqueId())) { + this.noticeService.create() + .player(admin.getUniqueId()) + .notice(translation -> translation.helpOp().playerNotSentHelpOp()) + .placeholder("{PLAYER}", target.getName()) + .send(); + + return; + } + + HelpOpReplyEvent event = new HelpOpReplyEvent(admin, target, message); + this.eventCaller.callEvent(event); + + if (event.isCancelled()) { + return; + } + + String escapedMessage = MiniMessage.miniMessage().escapeTags(event.getContent()); + + this.noticeService.create() + .console() + .player(target.getUniqueId()) + .onlinePlayers(HelpOpCommand.HELPOP_SPY) + .notice(translation -> translation.helpOp().adminReply()) + .placeholder("{ADMIN}", admin.getName()) + .placeholder("{PLAYER}", target.getName()) + .placeholder("{TEXT}", escapedMessage) + .send(); + + this.noticeService.create() + .player(admin.getUniqueId()) + .notice(translation -> translation.helpOp().adminReplySend()) + .placeholder("{PLAYER}", target.getName()) + .send(); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpServiceImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpServiceImpl.java new file mode 100644 index 000000000..7ac162841 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpServiceImpl.java @@ -0,0 +1,26 @@ +package com.eternalcode.core.feature.helpop; + +import com.eternalcode.core.injector.annotations.component.Service; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import java.time.Duration; +import java.util.UUID; +import org.jspecify.annotations.NonNull; + +@Service +class HelpOpServiceImpl implements HelpOpService { + + private final Cache helpOpSenders = CacheBuilder.newBuilder() + .expireAfterWrite(Duration.ofHours(1)) + .build(); + + @Override + public void markSender(@NonNull UUID playerUuid) { + this.helpOpSenders.put(playerUuid, true); + } + + @Override + public boolean hasSentHelpOp(@NonNull UUID playerUuid) { + return this.helpOpSenders.getIfPresent(playerUuid) != null; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/ENHelpOpMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/ENHelpOpMessages.java index aacc7e81e..b067a3af1 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/ENHelpOpMessages.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/ENHelpOpMessages.java @@ -1,18 +1,34 @@ package com.eternalcode.core.feature.helpop.messages; +import com.eternalcode.multification.bukkit.notice.BukkitNotice; import com.eternalcode.multification.notice.Notice; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; import lombok.Getter; import lombok.experimental.Accessors; +import org.bukkit.Sound; @Getter @Accessors(fluent = true) public class ENHelpOpMessages extends OkaeriConfig implements HelpOpSection { @Comment("# {PLAYER} - Player who send message on /helpop, {TEXT} - message") Notice format = Notice.chat("[HelpOp] {PLAYER}: {TEXT}"); + @Comment(" ") Notice send = Notice.chat("This message has been successfully sent to administration"); + @Comment("# {TIME} - Time to next use (cooldown)") Notice helpOpDelay = Notice.chat("You can use this command for: {TIME}"); + + @Comment("# {ADMIN} - Admin who replied, {PLAYER} - Player who received the reply, {TEXT} - Reply message") + Notice adminReply = BukkitNotice.builder() + .chat("[HelpOp] {ADMIN} -> {PLAYER}: {TEXT}") + .sound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f) + .build(); + + @Comment(" ") + Notice adminReplySend = Notice.chat("Reply has been sent to player {PLAYER}"); + + @Comment("# {PLAYER} - Player name") + Notice playerNotSentHelpOp = Notice.chat("Player {PLAYER} has not sent any helpop request"); } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/HelpOpSection.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/HelpOpSection.java index 5c1c8c84f..f190b39f8 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/HelpOpSection.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/HelpOpSection.java @@ -6,4 +6,7 @@ public interface HelpOpSection { Notice format(); Notice send(); Notice helpOpDelay(); + Notice adminReply(); + Notice adminReplySend(); + Notice playerNotSentHelpOp(); } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/PLHelpOpMessages.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/PLHelpOpMessages.java index 27b5e89ff..5b6f145f9 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/PLHelpOpMessages.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/messages/PLHelpOpMessages.java @@ -1,10 +1,12 @@ package com.eternalcode.core.feature.helpop.messages; +import com.eternalcode.multification.bukkit.notice.BukkitNotice; import com.eternalcode.multification.notice.Notice; import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; import lombok.Getter; import lombok.experimental.Accessors; +import org.bukkit.Sound; @Getter @Accessors(fluent = true) @@ -12,8 +14,22 @@ public class PLHelpOpMessages extends OkaeriConfig implements HelpOpSection { @Comment({ "# {PLAYER} - Gracz który wysłał wiadomość na helpop, {TEXT} - Treść wysłanej wiadomości" }) Notice format = Notice .chat("[HelpOp] {PLAYER}: {TEXT}"); + @Comment(" ") Notice send = Notice.chat("Wiadomość została wysłana do administracji"); + @Comment("# {TIME} - Czas do końca blokady (cooldown)") Notice helpOpDelay = Notice.chat("Możesz użyć tej komendy dopiero za {TIME}!"); + + @Comment("# {ADMIN} - Admin który odpowiedział, {PLAYER} - Gracz który otrzymał odpowiedź, {TEXT} - Treść odpowiedzi") + Notice adminReply = BukkitNotice.builder() + .chat("[HelpOp] {ADMIN} -> {PLAYER}: {TEXT}") + .sound(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f) + .build(); + + @Comment(" ") + Notice adminReplySend = Notice.chat("Odpowiedź została wysłana do gracza {PLAYER}"); + + @Comment("# {PLAYER} - Nazwa gracza") + Notice playerNotSentHelpOp = Notice.chat("Gracz {PLAYER} nie wysłał żadnego zapytania na helpop"); }