diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommand.java new file mode 100644 index 000000000..872cb9b59 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommand.java @@ -0,0 +1,36 @@ +package com.eternalcode.core.feature.customcommand; + +import com.eternalcode.multification.notice.Notice; +import java.io.Serializable; +import java.util.List; + +public class CustomCommand implements Serializable { + + private String name; + private List aliases; + private Notice message; + + public CustomCommand() {} + + public CustomCommand(String name, List aliases, Notice message) { + this.name = name; + this.aliases = aliases; + this.message = message; + } + + public static CustomCommand of(String commandName, List aliases, Notice message) { + return new CustomCommand(commandName, aliases, message); + } + + public String getName() { + return name; + } + + public List getAliases() { + return aliases; + } + + public Notice getMessage() { + return message; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandBukkitWrapper.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandBukkitWrapper.java new file mode 100644 index 000000000..dbf2e6099 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandBukkitWrapper.java @@ -0,0 +1,40 @@ +package com.eternalcode.core.feature.customcommand; + +import com.eternalcode.core.notice.NoticeService; +import com.eternalcode.multification.notice.Notice; +import dev.rollczi.litecommands.util.StringUtil; +import java.util.List; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class CustomCommandBukkitWrapper extends Command { + + private static final String EMPTY_USAGE_MESSAGE = StringUtil.EMPTY; + private static final String EMPTY_DESCRIPTION_MESSAGE = StringUtil.EMPTY; + + private final NoticeService noticeService; + private final Notice message; + + protected CustomCommandBukkitWrapper( + @NotNull String name, + @NotNull List aliases, + NoticeService noticeService, + Notice message + ) { + super(name, EMPTY_DESCRIPTION_MESSAGE, EMPTY_USAGE_MESSAGE, aliases); + + this.noticeService = noticeService; + this.message = message; + } + + @Override + public boolean execute(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] strings) { + this.noticeService.create() + .notice(this.message) + .sender(commandSender) + .send(); + + return true; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandConfig.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandConfig.java new file mode 100644 index 000000000..eace14c89 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandConfig.java @@ -0,0 +1,133 @@ +package com.eternalcode.core.feature.customcommand; + +import com.eternalcode.core.configuration.AbstractConfigurationFile; +import com.eternalcode.core.injector.annotations.component.ConfigurationFile; +import com.eternalcode.multification.notice.Notice; +import eu.okaeri.configs.annotation.Comment; +import eu.okaeri.configs.annotation.Header; +import java.io.File; +import java.time.Duration; +import java.util.List; +import net.kyori.adventure.bossbar.BossBar.Color; +import net.kyori.adventure.bossbar.BossBar.Overlay; + +@ConfigurationFile +@Header({ + " ", + "Custom commands configuration", + "", + "This file allows you to define your own commands using simple YAML configuration.", + "Each command entry supports multiple display formats: chat, title, subtitle, action bar, bossbar and sound.", + "", + "Example usage:", + "- Display your Discord link with a title and chat message (/discord)", + "- Promote your store using a bossbar (/store)", + "- Show server rules with a subtitle and timed title (/rules)", + "", + "You can test and generate these easily using:", + "https://www.eternalcode.pl/notification-generator" +}) +public class CustomCommandConfig extends AbstractConfigurationFile { + + @Comment({ + " ", + "# Custom commands", + "#", + "# Each command defines its name, aliases and message configuration.", + "#", + "# WARNING: You must restart the server after editing this file for changes to take effect.", + }) + public List commands = List.of( + CustomCommand.of( + "help", + List.of("info"), + Notice.builder() + .title("Welcome to EternalCore!") + .subtitle("Use /help to get started") + .times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)) + .actionBar("Need support? Try /discord") + .chat( + " ", + "To add your own commands, edit custom-commands.yml and restart the server.", + "Use our generator to create nice looking messages:", + "https://www.eternalcode.pl/notification-generator" + ).build() + ), + CustomCommand.of( + "discord", + List.of("dc"), + Notice.builder() + .title("🎧 Discord") + .subtitle("Join the community!") + .times(Duration.ofMillis(300), Duration.ofSeconds(2), Duration.ofMillis(300)) + .chat( + " ", + "🎧 Join our Discord server:", + "https://discord.gg/yourserver", + " ", + "Talk with the team, suggest features, get help." + ).build() + ), + CustomCommand.of( + "store", + List.of("shop"), + Notice.builder() + .bossBar( + Color.YELLOW, Overlay.NOTCHED_10, Duration.ofSeconds(5), 1.0, + "🛒 Visit our store – support the server & get cool perks!") + .chat( + " ", + "🛒 Visit our server store:", + "https://store.yourserver.com", + " ", + "Your support keeps us alive ❤" + ).build() + ), + CustomCommand.of( + "vote", + List.of("votes"), + Notice.builder() + .actionBar("★ Vote daily and earn rewards!") + .chat( + " ", + "★ Vote for our server and receive daily rewards!", + "https://yourserver.com/vote", + " ", + "Each vote helps us grow!" + ).build() + ), + CustomCommand.of( + "rules", + List.of(), + Notice.builder() + .title("📜 Server Rules") + .subtitle("Read before playing!") + .times(Duration.ofMillis(300), Duration.ofSeconds(3), Duration.ofMillis(300)) + .actionBar("No cheating, griefing or toxicity – be respectful!") + .chat( + " ", + "📜 Server Rules:", + "https://yourserver.com/rules", + " ", + "Breaking rules may result in a ban." + ).build() + ), + CustomCommand.of( + "map", + List.of("dynmap"), + Notice.builder() + .chat( + " ", + "🗺 Live Server Map:", + "https://map.yourserver.com", + " ", + "See what others are building in real time!" + ).build() + ) + ); + + @Override + public File getConfigFile(File dataFolder) { + return new File(dataFolder, "custom-commands.yml"); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandRegistry.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandRegistry.java new file mode 100644 index 000000000..6f5732525 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/customcommand/CustomCommandRegistry.java @@ -0,0 +1,65 @@ +package com.eternalcode.core.feature.customcommand; + +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Service; +import com.eternalcode.core.notice.NoticeService; +import dev.rollczi.litecommands.util.StringUtil; +import java.lang.reflect.Field; +import org.bukkit.Server; +import org.bukkit.command.CommandMap; + +@Service +public class CustomCommandRegistry { + + private static final String FALLBACK_PREFIX = "eternalcore"; + + private final CustomCommandConfig customCommandConfig; + private final NoticeService noticeService; + private final Server server; + + private CommandMap commandMap; + + @Inject + public CustomCommandRegistry(CustomCommandConfig customCommandConfig, NoticeService noticeService, Server server) { + this.customCommandConfig = customCommandConfig; + this.noticeService = noticeService; + this.server = server; + + this.registerCustomCommands(); + } + + public void registerCustomCommands() { + for (CustomCommand customCommand : this.customCommandConfig.commands) { + this.registerCustomCommand(customCommand); + } + } + + private void registerCustomCommand(CustomCommand customCommand) { + CustomCommandBukkitWrapper customCommandBukkitWrapper = new CustomCommandBukkitWrapper( + customCommand.getName(), + customCommand.getAliases(), + this.noticeService, + customCommand.getMessage() + ); + + this.commandMap().register(FALLBACK_PREFIX, customCommandBukkitWrapper); + } + + CommandMap commandMap() { + if (this.commandMap == null) { + try { + Field commandMapField = this.server.getClass().getDeclaredField("commandMap"); + commandMapField.setAccessible(true); + + this.commandMap = (CommandMap) commandMapField.get(this.server); + } + catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException( + "Failed to get CommandMap from the server, this might be due to a server version incompatibility.", + exception); + } + } + + return this.commandMap; + } +}