diff --git a/.github/workflows/modrinth-publish.yml b/.github/workflows/modrinth-publish.yml new file mode 100644 index 000000000..e144be76c --- /dev/null +++ b/.github/workflows/modrinth-publish.yml @@ -0,0 +1,25 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: NookureStaff Modrinth Publish + +on: + push: + branches: [ release/*, dev, feature/*, fix/* ] +jobs: + build-on-ubuntu: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout main branch from GitHub + uses: actions/checkout@v4 + + - name: Execute Gradle build & publish + env: + MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} + run: | + chmod +x gradlew + ./gradlew :modrinth \ No newline at end of file diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisPublish.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisPublish.java deleted file mode 100644 index cda864a8e..000000000 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisPublish.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.nookure.staff.api.annotation; - -import com.google.inject.BindingAnnotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -@BindingAnnotation -public @interface RedisPublish { -} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisSubscribe.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisSubscribe.java deleted file mode 100644 index 0c1eaf554..000000000 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/annotation/RedisSubscribe.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.nookure.staff.api.annotation; - -import com.google.inject.BindingAnnotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -@BindingAnnotation -public @interface RedisSubscribe { -} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/command/StaffCommand.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/command/StaffCommand.java index 653f823db..ccbdc6747 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/command/StaffCommand.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/command/StaffCommand.java @@ -1,7 +1,12 @@ package com.nookure.staff.api.command; +import com.google.inject.Inject; +import com.nookure.staff.api.PlayerWrapper; import com.nookure.staff.api.StaffPlayerWrapper; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -14,15 +19,32 @@ *

*/ public abstract class StaffCommand extends Command { + private final PlayerTransformer transformer; + private final ConfigurationContainer messages; + + @Inject + public StaffCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages + ) { + this.transformer = transformer; + this.messages = messages; + } + @Override public void onCommand(@NotNull CommandSender sender, @NotNull String label, @NotNull List args) { if (sender instanceof StaffPlayerWrapper) { onStaffCommand((StaffPlayerWrapper) sender, label, args); + return; + } + if (sender.hasPermission("nookure.staff") && sender instanceof PlayerWrapper player) { + transformer.player2Staff(player.getUniqueId()); + player.sendMiniMessage(messages.get().youAreNowAnStaff()); return; } - sender.sendMiniMessage("Only staff members can execute this command."); + sender.sendMiniMessage(messages.get().onlyStaffMembersCanExecuteThisCommand()); } /** diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitConfig.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitConfig.java index 6ce877ba9..8cdd51437 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitConfig.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitConfig.java @@ -1,5 +1,6 @@ package com.nookure.staff.api.config.bukkit; +import com.nookure.staff.api.config.bukkit.partials.PermissionConfigPartial; import com.nookure.staff.api.config.bukkit.partials.config.*; import com.nookure.staff.api.config.partials.DatabaseConfig; import org.spongepowered.configurate.objectmapping.ConfigSerializable; @@ -25,6 +26,9 @@ public class BukkitConfig { """) public StaffModePartial staffMode = new StaffModePartial(); + @Setting + public PermissionConfigPartial permission = new PermissionConfigPartial(); + @Setting @Comment(""" Here you can configure some settings for the freeze module diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitMessages.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitMessages.java index aada7e480..31b88ca44 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitMessages.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/BukkitMessages.java @@ -19,7 +19,7 @@ public class BukkitMessages { """ The prefix for all staff messages. If you want to use the prefix in a message, use {prefix}. - """ + """ ) private String prefix = "Staff ยป"; @Setting @@ -43,6 +43,18 @@ public class BukkitMessages { """) private String noPermission = "{prefix} You don't have permission to do that."; + @Setting + @Comment("The message when a player has been registered as a staff member during the command execution.") + private String youAreNowAnStaffDuringCommandExecution = "{prefix} You have been just registered as a staff member, please redo the command in order to use it."; + + @Setting + @Comment("The message when a player has been registered as a staff member during the permission interceptor.") + private String youAreNowAnStaffDuringPermissionInterceptor = "{prefix} Congratulations!, welcome to the Staff members."; + + @Setting + @Comment("The message when only staff members can execute a command.") + private String onlyStaffMembersCanExecuteThisCommand = "{prefix} Only staff members can execute this command."; + public String prefix() { return prefix; } @@ -58,4 +70,16 @@ public String playerNotFound() { public String noPermission() { return noPermission; } + + public String youAreNowAnStaff() { + return youAreNowAnStaffDuringCommandExecution; + } + + public String youAreNowAnStaffDuringPermissionInterceptor() { + return youAreNowAnStaffDuringPermissionInterceptor; + } + + public String onlyStaffMembersCanExecuteThisCommand() { + return onlyStaffMembersCanExecuteThisCommand; + } } diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/GlowConfig.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/GlowConfig.java new file mode 100644 index 000000000..6dc9ffa6c --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/GlowConfig.java @@ -0,0 +1,30 @@ +package com.nookure.staff.api.config.bukkit; + +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +import java.util.Map; + +@ConfigSerializable +public class GlowConfig { + @Setting + @Comment("Enable or disable the glow feature") + public boolean enabled = true; + + @Setting + @Comment("Change the prefix color of the player's name") + public boolean tabIntegration = true; + + @Setting + @Comment("The default color for the glow") + public String defaultColor = ""; + + @Setting + @Comment("This colors must follow the format: or <#hex>") + public Map glowColors = Map.of( + "staff", "", + "admin", "", + "mod", "" + ); +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/ItemsConfig.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/ItemsConfig.java index 167ef4229..d2c49711e 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/ItemsConfig.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/ItemsConfig.java @@ -1,5 +1,7 @@ package com.nookure.staff.api.config.bukkit; +import com.nookure.staff.api.config.bukkit.partials.CustomItemPartial; +import com.nookure.staff.api.config.bukkit.partials.CustomItemType; import com.nookure.staff.api.config.bukkit.partials.ItemPartial; import com.nookure.staff.api.item.Items; import org.bukkit.Material; @@ -19,6 +21,9 @@ public static class StaffItems { @Setting private Map items = new HashMap<>(); + @Setting + private Map customItems = new HashMap<>(); + public StaffItems() { items.put(Items.RANDOM_PLAYER_TELEPORT.toString(), new ItemPartial( true, @@ -82,10 +87,36 @@ public StaffItems() { List.of("Click to teleport through", "The block you are looking at"), "nookurestaff.item.thru" )); + + customItems.put("custom_item", new CustomItemPartial( + false, + "Custom Item", + Material.DIAMOND, + 2, + List.of("Click to send hello world to the player that you are looking at"), + "nookurestaff.item.custom", + CustomItemType.COMMAND_TARGET_AS_PLAYER, + "msg {target} Hello, world!" + )); + + customItems.put("custom_item_2", new CustomItemPartial( + false, + "Custom Item 2", + Material.REDSTONE_BLOCK, + 6, + List.of("Click to kick the player that you are looking at"), + "nookurestaff.item.custom2", + CustomItemType.COMMAND_TARGET_AS_CONSOLE, + "kick {target} Hello, world!" + )); } public Map getItems() { return items; } + + public Map getCustomItems() { + return customItems; + } } } diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemPartial.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemPartial.java new file mode 100644 index 000000000..3e09474d6 --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemPartial.java @@ -0,0 +1,67 @@ +package com.nookure.staff.api.config.bukkit.partials; + +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +import java.util.List; + +@ConfigSerializable +public class CustomItemPartial extends ItemPartial { + @Setting + @Comment(""" + The type of the custom item. + - COMMAND_AS_PLAYER: The command will be executed as the player who clicked the item. + - COMMAND_AS_CONSOLE: The command will be executed as the console. + - COMMAND_TARGET_AS_PLAYER: The command will be executed + as the player who is staff but with the placeholder of {target} replaced by the player that + have been clicked. + - COMMAND_TARGET_AS_CONSOLE: The command will be executed as the console but with the placeholder + of {target} replaced by the player that have been clicked. + """) + private CustomItemType type; + + @Setting + @Comment(""" + The command to execute when the item is clicked. + You can use the placeholder {player} to get the player who clicked the item. + You can use the placeholder {target} to get the player who have been clicked. + """) + private String command; + + public CustomItemPartial() { + } + + public CustomItemPartial( + final boolean enabled, + @NotNull final String name, + @NotNull final Material material, + final int slot, + @NotNull final List lore, + @NotNull final String permission, + @NotNull final CustomItemType type, + @NotNull final String command + ) { + super(enabled, name, material, slot, lore, permission); + this.type = type; + this.command = command; + } + + public CustomItemType getType() { + return type; + } + + public void setType(CustomItemType type) { + this.type = type; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemType.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemType.java new file mode 100644 index 000000000..adc313f5e --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/CustomItemType.java @@ -0,0 +1,8 @@ +package com.nookure.staff.api.config.bukkit.partials; + +public enum CustomItemType { + COMMAND_AS_PLAYER, + COMMAND_AS_CONSOLE, + COMMAND_TARGET_AS_PLAYER, + COMMAND_TARGET_AS_CONSOLE, +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/PermissionConfigPartial.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/PermissionConfigPartial.java new file mode 100644 index 000000000..10f6403e2 --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/bukkit/partials/PermissionConfigPartial.java @@ -0,0 +1,12 @@ +package com.nookure.staff.api.config.bukkit.partials; + +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@ConfigSerializable +public class PermissionConfigPartial { + @Setting + @Comment("Listen when a player gets `nookure.staff` permission to reconstruct the player wrapper.") + public boolean watchLuckPermsPermissions = true; +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/messaging/MessengerConfig.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/messaging/MessengerConfig.java index d0da19cb1..68adb51f8 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/config/messaging/MessengerConfig.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/config/messaging/MessengerConfig.java @@ -4,27 +4,42 @@ import org.spongepowered.configurate.objectmapping.meta.Comment; import org.spongepowered.configurate.objectmapping.meta.Setting; +import java.util.UUID; + @ConfigSerializable public class MessengerConfig { @Setting @Comment( """ The configuration for the Redis messenger. - """ + """ ) - public final RedisPartial redis = new RedisPartial(); + public RedisPartial redis = new RedisPartial(); + + @Setting + @Comment("This UUID represents the server in SQL databases, ensure it's unique.") + public UUID serverId = UUID.randomUUID(); + @Setting @Comment( """ The type of messenger to use. Here are the options: - - REDIS: Use Redis to send messages between servers (recommended) + - REDIS: Use Redis to send messages between servers, the most + reliable way to send messages between servers, but it requires + a Redis server to be installed and configured, it's the fastest. + - PM: Use plugin messages to send messages between servers this - will only work if all the servers are under the same proxy and - in the proxy is installed the bridge plugin. + will only work if all the servers are under the same proxy and + in the proxy is installed the bridge plugin, it's not the most + reliable way to send messages between servers. + + - MYSQL: Use MySQL to send messages between servers, a bit more + reliable than PM but much more slower than Redis. + - NONE: Disable the sync, useful when you don't have a network and you only have 1 server. - """ + """ ) private MessengerType type = MessengerType.NONE; @@ -35,6 +50,7 @@ public MessengerType getType() { public enum MessengerType { REDIS, PM, + MYSQL, NONE } } diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/extension/staff/GlowPlayerExtension.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/extension/staff/GlowPlayerExtension.java new file mode 100644 index 000000000..e5486c131 --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/extension/staff/GlowPlayerExtension.java @@ -0,0 +1,16 @@ +package com.nookure.staff.api.extension.staff; + +import com.google.inject.Inject; +import com.nookure.staff.api.StaffPlayerWrapper; +import com.nookure.staff.api.extension.StaffPlayerExtension; +import org.jetbrains.annotations.NotNull; + +public abstract class GlowPlayerExtension extends StaffPlayerExtension { + @Inject + public GlowPlayerExtension(@NotNull StaffPlayerWrapper player) { + super(player); + } + + @NotNull + public abstract String getGlowColor(); +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/hook/PermissionHook.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/hook/PermissionHook.java new file mode 100644 index 000000000..5eba51e75 --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/hook/PermissionHook.java @@ -0,0 +1,8 @@ +package com.nookure.staff.api.hook; + +import com.nookure.staff.api.PlayerWrapper; +import org.jetbrains.annotations.NotNull; + +public interface PermissionHook { + @NotNull String getHighestGroup(@NotNull PlayerWrapper player); +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/manager/PlayerWrapperManager.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/manager/PlayerWrapperManager.java index daf27ee03..9f956a2ad 100644 --- a/NookureStaff-API/src/main/java/com/nookure/staff/api/manager/PlayerWrapperManager.java +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/manager/PlayerWrapperManager.java @@ -8,6 +8,7 @@ import com.nookure.staff.api.PlayerWrapper; import com.nookure.staff.api.StaffPlayerWrapper; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.stream.Stream; @@ -60,6 +61,18 @@ public Optional getPlayerWrapper(@NotNull UUID uuid) { return Optional.ofNullable(playerWrappersByUUID.get(uuid)); } + /** + * Gets a player wrapper by its unique id. + * + * @param uuid the unique id of the player wrapper + * @return the player wrapper if it exists, otherwise null + */ + @Nullable + public PlayerWrapper getPlayerWrapperOrNull(@NotNull UUID uuid) { + Objects.requireNonNull(uuid, "UUID cannot be null"); + return playerWrappersByUUID.get(uuid); + } + /** * Gets a staff player by its player class. * diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/NameTagTransformer.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/NameTagTransformer.java new file mode 100644 index 000000000..8119191ff --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/NameTagTransformer.java @@ -0,0 +1,14 @@ +package com.nookure.staff.api.util.transformer; + +import com.nookure.staff.api.PlayerWrapper; +import org.jetbrains.annotations.NotNull; + +public interface NameTagTransformer { + void setPrefix(@NotNull PlayerWrapper player, @NotNull String prefix); + + void removePrefix(@NotNull PlayerWrapper player); + + void setSuffix(@NotNull PlayerWrapper player, @NotNull String suffix); + + void removeSuffix(@NotNull PlayerWrapper player); +} diff --git a/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/PlayerTransformer.java b/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/PlayerTransformer.java new file mode 100644 index 000000000..1b3a9390e --- /dev/null +++ b/NookureStaff-API/src/main/java/com/nookure/staff/api/util/transformer/PlayerTransformer.java @@ -0,0 +1,21 @@ +package com.nookure.staff.api.util.transformer; + +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public interface PlayerTransformer { + /** + * Transform a player to a staff member + * + * @param uuid The player's UUID + */ + void player2Staff(@NotNull final UUID uuid); + + /** + * Transform a staff member to a player + * + * @param uuid The player's UUID + */ + void staff2player(@NotNull final UUID uuid); +} diff --git a/NookureStaff-Common/src/main/java/com/nookure/staff/lib/DefaultLibRepo.java b/NookureStaff-Common/src/main/java/com/nookure/staff/lib/DefaultLibRepo.java index f01347f67..e12893fdd 100644 --- a/NookureStaff-Common/src/main/java/com/nookure/staff/lib/DefaultLibRepo.java +++ b/NookureStaff-Common/src/main/java/com/nookure/staff/lib/DefaultLibRepo.java @@ -14,7 +14,7 @@ private DefaultLibRepo() { Library hikariCP = Library.builder() .groupId("com{}zaxxer") .artifactId("HikariCP") - .version("4.0.3") + .version("6.2.1") .relocate("com{}zaxxer", "com{}nookure{}staff{}libs") .isolatedLoad(false) .build(); @@ -22,7 +22,7 @@ private DefaultLibRepo() { Library jedis = Library.builder() .groupId("redis{}clients") .artifactId("jedis") - .version("4.4.0-m2") + .version("5.2.0") .isolatedLoad(false) .build(); diff --git a/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/RedisMessenger.java b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/RedisMessenger.java index 2a1281896..c17310dcf 100644 --- a/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/RedisMessenger.java +++ b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/RedisMessenger.java @@ -5,14 +5,13 @@ import com.google.inject.Singleton; import com.nookure.staff.api.Logger; import com.nookure.staff.api.PlayerWrapper; -import com.nookure.staff.api.annotation.RedisPublish; -import com.nookure.staff.api.annotation.RedisSubscribe; import com.nookure.staff.api.event.EventManager; import com.nookure.staff.api.messaging.EventMessenger; import com.nookure.staff.api.util.Scheduler; import org.jetbrains.annotations.NotNull; import redis.clients.jedis.BinaryJedisPubSub; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; import java.util.Arrays; @@ -20,11 +19,7 @@ public class RedisMessenger extends EventMessenger { private static final byte[] CHANNEL = "nkstaff_events".getBytes(); @Inject - @RedisPublish - private Jedis jedisPublish; - @Inject - @RedisSubscribe - private Jedis jedisSubscribe; + private JedisPool jedisPool; @Inject private Injector injector; @Inject @@ -36,27 +31,26 @@ public class RedisMessenger extends EventMessenger { @Override public void prepare() { - if (!jedisPublish.isConnected()) { - logger.debug("Connecting to redis publish..."); - jedisPublish.connect(); - } - - if (!jedisSubscribe.isConnected()) { - logger.debug("Connecting to redis subscribe..."); - jedisSubscribe.connect(); - } - logger.debug("Subscribing to redis channel " + Arrays.toString(CHANNEL)); messenger = injector.getInstance(RedisEventMessenger.class); - subscribeTaskID = scheduler.async(() -> jedisSubscribe.subscribe(messenger, CHANNEL)); + subscribeTaskID = scheduler.async(() -> { + try (Jedis jedis = jedisPool.getResource()) { + jedis.subscribe(messenger, CHANNEL); + } catch (Exception e) { + logger.severe("Failed to subscribe to redis channel " + Arrays.toString(CHANNEL)); + throw new RuntimeException(e); + } + }); } @Override - @SuppressWarnings("SynchronizeOnNonFinalField") public void publish(@NotNull PlayerWrapper sender, byte @NotNull [] data) { - synchronized (jedisPublish) { + try (Jedis jedis = jedisPool.getResource()) { logger.debug("Publishing event to redis"); - jedisPublish.publish(CHANNEL, data); + jedis.publish(CHANNEL, data); + } catch (Exception e) { + logger.severe("Failed to publish event to redis"); + throw new RuntimeException(e); } } @@ -64,9 +58,7 @@ public void publish(@NotNull PlayerWrapper sender, byte @NotNull [] data) { public void close() throws Exception { messenger.unsubscribe(); scheduler.cancel(subscribeTaskID); - - jedisSubscribe.close(); - jedisPublish.close(); + jedisPool.close(); } private static class RedisEventMessenger extends BinaryJedisPubSub { diff --git a/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLMessenger.java b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLMessenger.java new file mode 100644 index 000000000..ed7e28a7a --- /dev/null +++ b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLMessenger.java @@ -0,0 +1,76 @@ +package com.nookure.staff.messaging.sql; + +import com.google.inject.Inject; +import com.nookure.staff.api.Logger; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.messaging.MessengerConfig; +import com.nookure.staff.api.database.util.MigrationUtil; +import com.nookure.staff.api.event.EventManager; +import com.nookure.staff.api.messaging.EventMessenger; +import org.jetbrains.annotations.NotNull; + +import javax.sql.DataSource; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +public final class SQLMessenger extends EventMessenger { + private final Logger logger; + private final AtomicReference dataSource; + private final ConfigurationContainer config; + private final EventManager eventManager; + + private static final String INSERT_QUERY = "INSERT INTO nookure_staff_messenger (data, server_uuid) VALUES (?, ?)"; + + @Inject + public SQLMessenger( + @NotNull final AtomicReference dataSource, + @NotNull final Logger logger, + @NotNull final ConfigurationContainer config, + @NotNull final EventManager eventManager + ) { + this.dataSource = dataSource; + this.logger = logger; + this.config = config; + this.eventManager = eventManager; + } + + @Override + public void prepare() { + final var query = MigrationUtil.fromClasspath("migrations/mysql/21_55_16_12_2025_sql_messenger_system.sql"); + + try (var connection = this.dataSource.get().getConnection()) { + final var statement = connection.prepareStatement(query); + statement.execute(); + connection.commit(); + } catch (final Exception e) { + logger.severe("Failed to prepare SQLMessenger"); + throw new RuntimeException(e); + } + } + + @Override + public void publish(@NotNull PlayerWrapper sender, byte @NotNull [] data) { + CompletableFuture.runAsync(() -> this.insert(data)) + .thenRun(() -> logger.debug("Published event to SQLMessenger")); + + decodeEvent(data).ifPresent(eventManager::fireEvent); + } + + public void insert(byte @NotNull [] data) { + try (var connection = this.dataSource.get().getConnection()) { + final var statement = connection.prepareStatement(INSERT_QUERY); + final var blob = connection.createBlob(); + blob.setBytes(1, data); + statement.setBlob(1, blob); + + statement.setString(2, config.get().serverId.toString()); + + statement.execute(); + connection.commit(); + } catch (final Exception e) { + logger.severe("Failed to publish event to SQLMessenger"); + throw new RuntimeException(e); + } + } +} diff --git a/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLPollTask.java b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLPollTask.java new file mode 100644 index 000000000..4c7543283 --- /dev/null +++ b/NookureStaff-Common/src/main/java/com/nookure/staff/messaging/sql/SQLPollTask.java @@ -0,0 +1,114 @@ +package com.nookure.staff.messaging.sql; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.nookure.staff.api.Logger; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.messaging.MessengerConfig; +import com.nookure.staff.api.event.EventManager; +import com.nookure.staff.api.messaging.EventMessenger; +import org.jetbrains.annotations.NotNull; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +@Singleton +public final class SQLPollTask { + private final AtomicReference dataSource; + private final Logger logger; + private final EventMessenger eventMessenger; + private final EventManager eventManager; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final ConfigurationContainer config; + + private static final String LAST_POLL_QUERY = "SELECT MAX(`id`) as `latest` FROM `nookure_staff_messenger`"; + private static final String POLL_QUERY = "SELECT * FROM `nookure_staff_messenger` WHERE `id` > ? AND (NOW() - `time` < 30)"; + private static final String CLEANUP_QUERY = "DELETE FROM `nookure_staff_messenger` WHERE `time` < (NOW() - 60)"; + + private long lastPoll = -1; + + @Inject + public SQLPollTask( + @NotNull final AtomicReference dataSource, + @NotNull final Logger logger, + @NotNull final EventMessenger eventMessenger, + @NotNull final EventManager eventManager, + @NotNull final ConfigurationContainer config + ) { + this.dataSource = dataSource; + this.logger = logger; + this.eventMessenger = eventMessenger; + this.eventManager = eventManager; + this.config = config; + + if (!(eventMessenger instanceof SQLMessenger)) { + throw new IllegalArgumentException("EventMessenger must be an instance of SQLMessenger"); + } + + try (var connection = dataSource.get().getConnection()) { + ResultSet rs = connection.prepareStatement(LAST_POLL_QUERY).executeQuery(); + if (rs.next()) { + lastPoll = rs.getLong("latest"); + } + } catch (SQLException e) { + logger.severe("Failed to poll SQLMessenger"); + throw new RuntimeException(e); + } + } + + public void pollMessages() { + long start = System.currentTimeMillis(); + logger.debug("Polling SQLMessenger..."); + + lock.writeLock().lock(); + try (var connection = dataSource.get().getConnection()) { + var statement = connection.prepareStatement(POLL_QUERY); + statement.setLong(1, lastPoll); + var rs = statement.executeQuery(); + while (rs.next()) { + final var uuid = rs.getString("server_uuid"); + + if (uuid.equals(config.get().serverId.toString())) { + continue; + } + + final var blob = rs.getBlob("data"); + final var data = blob.getBytes(1, (int) blob.length()); + eventMessenger.decodeEvent(data).ifPresent(event -> { + logger.debug("Received event " + event.getClass().getSimpleName() + " from redis"); + eventManager.fireEvent(event); + }); + lastPoll = rs.getLong("id"); + } + } catch (SQLException e) { + logger.severe("Failed to poll SQLMessenger"); + throw new RuntimeException(e); + } finally { + lock.writeLock().unlock(); + } + + logger.debug("Finished polling SQLMessenger in " + (System.currentTimeMillis() - start) + "ms"); + } + + public void cleanup() { + logger.debug("Cleaning up SQLMessenger..."); + long start = System.currentTimeMillis(); + + lock.writeLock().lock(); + try (var connection = dataSource.get().getConnection()) { + connection.prepareStatement(CLEANUP_QUERY).execute(); + connection.commit(); + } catch (SQLException e) { + logger.severe("Failed to cleanup SQLMessenger"); + throw new RuntimeException(e); + } finally { + lock.writeLock().unlock(); + } + + logger.debug("Finished cleaning up SQLMessenger in " + (System.currentTimeMillis() - start) + "ms"); + } +} diff --git a/NookureStaff-Common/src/main/resources/migrations/mysql/21_55_16_12_2025_sql_messenger_system.sql b/NookureStaff-Common/src/main/resources/migrations/mysql/21_55_16_12_2025_sql_messenger_system.sql new file mode 100644 index 000000000..86baff24f --- /dev/null +++ b/NookureStaff-Common/src/main/resources/migrations/mysql/21_55_16_12_2025_sql_messenger_system.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `nookure_staff_messenger` +( + `id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `server_uuid` varchar(36) NOT NULL, + `data` BLOB NOT NULL +); diff --git a/NookureStaff-Paper/build.gradle.kts b/NookureStaff-Paper/build.gradle.kts index a8502c727..02034fc2d 100644 --- a/NookureStaff-Paper/build.gradle.kts +++ b/NookureStaff-Paper/build.gradle.kts @@ -18,6 +18,8 @@ dependencies { compileOnly(libs.placeholderApi) compileOnly(libs.lucko.commodore) compileOnly(libs.nookure.core.inventory) + compileOnly(libs.lucko.luckperms) + compileOnly(libs.neznamy.tab.api) bukkitLibrary(libs.guice) bukkitLibrary(libs.google.guice.assistedinject) paperLibrary(libs.guice) @@ -52,7 +54,7 @@ bukkit { website = "https://angelillo15.es/" authors = listOf("Angelillo15") main = "com.nookure.staff.paper.bootstrap.StaffBootstrapper" - softDepend = listOf("PlaceholderAPI") + softDepend = listOf("PlaceholderAPI", "LuckPerms", "SuperVanish", "PremiumVanish", "TAB") } paper { @@ -68,14 +70,27 @@ paper { register("PlaceholderAPI") { required = false load = PaperPluginDescription.RelativeLoadOrder.BEFORE + joinClasspath = true } register("SuperVanish") { required = false load = PaperPluginDescription.RelativeLoadOrder.BEFORE + joinClasspath = true } register("PremiumVanish") { required = false load = PaperPluginDescription.RelativeLoadOrder.BEFORE + joinClasspath = true + } + register("LuckPerms") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + joinClasspath = true + } + register("TAB") { + required = false + load = PaperPluginDescription.RelativeLoadOrder.BEFORE + joinClasspath = true } } } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/NookureStaff.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/NookureStaff.java index 5b71f6b3f..3bc822630 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/NookureStaff.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/NookureStaff.java @@ -7,17 +7,16 @@ import com.nookure.staff.api.addons.AddonManager; import com.nookure.staff.api.command.Command; import com.nookure.staff.api.config.ConfigurationContainer; -import com.nookure.staff.api.config.bukkit.BukkitConfig; -import com.nookure.staff.api.config.bukkit.BukkitMessages; -import com.nookure.staff.api.config.bukkit.ItemsConfig; -import com.nookure.staff.api.config.bukkit.StaffModeBlockedCommands; +import com.nookure.staff.api.config.bukkit.*; import com.nookure.staff.api.config.bukkit.partials.VanishType; import com.nookure.staff.api.config.bukkit.partials.messages.note.NoteMessages; import com.nookure.staff.api.config.messaging.MessengerConfig; import com.nookure.staff.api.database.AbstractPluginConnection; +import com.nookure.staff.api.database.DataProvider; import com.nookure.staff.api.event.EventManager; import com.nookure.staff.api.extension.StaffPlayerExtensionManager; import com.nookure.staff.api.extension.VanishExtension; +import com.nookure.staff.api.extension.staff.GlowPlayerExtension; import com.nookure.staff.api.extension.staff.StaffModeExtension; import com.nookure.staff.api.manager.PlayerWrapperManager; import com.nookure.staff.api.messaging.Channels; @@ -28,6 +27,7 @@ import com.nookure.staff.paper.command.main.NookureStaffCommand; import com.nookure.staff.paper.command.staff.StaffCommandParent; import com.nookure.staff.paper.extension.FreezePlayerExtension; +import com.nookure.staff.paper.extension.staff.PaperGlowPlayerExtension; import com.nookure.staff.paper.extension.staff.PaperStaffModeExtension; import com.nookure.staff.paper.extension.vanish.InternalVanishExtension; import com.nookure.staff.paper.extension.vanish.SuperVanishExtension; @@ -49,13 +49,11 @@ import com.nookure.staff.paper.listener.staff.state.*; import com.nookure.staff.paper.listener.staff.vanish.PlayerVanishListener; import com.nookure.staff.paper.listener.staff.vanish.StaffVanishListener; -import com.nookure.staff.paper.loader.AddonsLoader; -import com.nookure.staff.paper.loader.InventoryLoader; -import com.nookure.staff.paper.loader.ItemsLoader; -import com.nookure.staff.paper.loader.PlaceholderApiLoader; +import com.nookure.staff.paper.loader.*; import com.nookure.staff.paper.messaging.BackendMessageMessenger; import com.nookure.staff.paper.note.command.ParentNoteCommand; import com.nookure.staff.paper.note.listener.OnPlayerNoteJoin; +import com.nookure.staff.paper.permission.LuckPermsPermissionInterceptor; import com.nookure.staff.paper.pin.command.ChangePin; import com.nookure.staff.paper.pin.command.DeletePinCommand; import com.nookure.staff.paper.pin.command.SetPinCommand; @@ -70,6 +68,7 @@ import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; +import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -77,6 +76,8 @@ @Singleton public class NookureStaff { private final ArrayList listeners = new ArrayList<>(); + private final ArrayList closeableListeners = new ArrayList<>(); + private final List> loadersClass; private final List loaders = new ArrayList<>(); @@ -104,6 +105,8 @@ public class NookureStaff { @Inject private ConfigurationContainer noteMessages; @Inject + private ConfigurationContainer glowConfig; + @Inject private JavaPlugin plugin; @Inject private Injector injector; @@ -141,6 +144,11 @@ private void loadListeners() { injector.getInstance(EventMessenger.class).prepare(); logger.debug("Redis messenger registered"); } + case MYSQL -> { + logger.debug("Registering MySQL messenger..."); + injector.getInstance(EventMessenger.class).prepare(); + logger.debug("MySQL messenger registered"); + } case NONE -> logger.debug("No messenger type was found, events will not be sent"); } @@ -218,6 +226,24 @@ private void loadBukkitListeners() { if (config.get().modules.isPinCode()) { registerListener(OnInventoryClose.class); } + + if (config.get().permission.watchLuckPermsPermissions) { + try { + Class.forName("net.luckperms.api.LuckPerms"); + closeableListeners.add(injector.getInstance(LuckPermsPermissionInterceptor.class)); + } catch (ClassNotFoundException e) { + logger.warning("LuckPerms is not installed on the server, disabling LuckPerms permission interceptor"); + } + } + + if (messengerConfig.get().getType() == MessengerConfig.MessengerType.MYSQL) { + if (config.get().database.getType() != DataProvider.MYSQL) { + logger.severe("MySQL messenger requires MySQL database, disabling plugin"); + Bukkit.getPluginManager().disablePlugin(plugin); + } + + closeableListeners.add(injector.getInstance(SQLPollTaskLoader.class)); + } } public void registerListener(Class listener) { @@ -229,9 +255,22 @@ public void registerListener(Class listener) { } public void unregisterListeners() { + logger.debug("Unregistering Bukkit listeners..."); HandlerList.getHandlerLists().forEach(listener -> listeners.forEach(listener::unregister)); - listeners.clear(); + + logger.debug("Closing closeables listeners..."); + closeableListeners.forEach(closeable -> { + try { + closeable.close(); + } catch (Exception e) { + logger.severe("An error occurred while closing listener %s, %s", closeable.getClass().getName(), e); + } + }); + + closeableListeners.clear(); + + logger.debug("Listeners unregistered"); } private void loadLoaders() { @@ -322,6 +361,10 @@ private void loadExtensions() { if (config.get().modules.isStaffMode()) { extensionManager.registerExtension(PaperStaffModeExtension.class, StaffModeExtension.class); } + + if (glowConfig.get().enabled) { + extensionManager.registerExtension(PaperGlowPlayerExtension.class, GlowPlayerExtension.class); + } } public void onDisable() { @@ -343,7 +386,8 @@ public void reload() { messagesConfig, itemsConfig, staffModeBlockedCommands, - noteMessages + noteMessages, + glowConfig ).forEach(c -> c.reload().join()); unregisterListeners(); diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/StaffPaperPlayerWrapper.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/StaffPaperPlayerWrapper.java index 6ef097b84..179a45a66 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/StaffPaperPlayerWrapper.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/StaffPaperPlayerWrapper.java @@ -142,7 +142,7 @@ public void checkVanishState() { logger.debug("StaffDataModel state: %s", staffDataModel); if (staffDataModel.vanished()) { - enableVanish(staffMode.get()); + enableVanish(true); } else { disableVanish(true); } @@ -183,11 +183,11 @@ public void writeVanishState(boolean state) { // // - private void enableStaffMode(boolean silentJoin) { + public void enableStaffMode(boolean silentJoin) { if (staffModeExtension != null) staffModeExtension.enableStaffMode(silentJoin); } - private void disableStaffMode() { + public void disableStaffMode() { if (staffModeExtension != null) staffModeExtension.disableStaffMode(); } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/bootstrap/PaperPluginModule.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/bootstrap/PaperPluginModule.java index 37be94337..c82bc2956 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/bootstrap/PaperPluginModule.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/bootstrap/PaperPluginModule.java @@ -7,14 +7,10 @@ import com.nookure.staff.api.NookureStaff; import com.nookure.staff.api.addons.AddonManager; import com.nookure.staff.api.annotation.PluginMessageMessenger; -import com.nookure.staff.api.annotation.RedisPublish; -import com.nookure.staff.api.annotation.RedisSubscribe; import com.nookure.staff.api.command.CommandManager; import com.nookure.staff.api.config.ConfigurationContainer; -import com.nookure.staff.api.config.bukkit.BukkitConfig; -import com.nookure.staff.api.config.bukkit.BukkitMessages; -import com.nookure.staff.api.config.bukkit.ItemsConfig; -import com.nookure.staff.api.config.bukkit.StaffModeBlockedCommands; +import com.nookure.staff.api.config.bukkit.*; +import com.nookure.staff.api.config.bukkit.partials.CustomItemPartial; import com.nookure.staff.api.config.bukkit.partials.messages.note.NoteMessages; import com.nookure.staff.api.config.common.CommandConfig; import com.nookure.staff.api.config.messaging.MessengerConfig; @@ -22,6 +18,7 @@ import com.nookure.staff.api.database.AbstractPluginConnection; import com.nookure.staff.api.database.repository.StaffStateRepository; import com.nookure.staff.api.extension.StaffPlayerExtensionManager; +import com.nookure.staff.api.hook.PermissionHook; import com.nookure.staff.api.manager.FreezeManager; import com.nookure.staff.api.manager.PlayerWrapperManager; import com.nookure.staff.api.manager.StaffItemsManager; @@ -29,6 +26,9 @@ import com.nookure.staff.api.placeholder.PlaceholderManager; import com.nookure.staff.api.service.PinUserService; import com.nookure.staff.api.service.UserNoteService; +import com.nookure.staff.api.state.PlayerState; +import com.nookure.staff.api.util.transformer.NameTagTransformer; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.api.util.PluginModule; import com.nookure.staff.api.util.Scheduler; import com.nookure.staff.api.util.ServerUtils; @@ -37,34 +37,46 @@ import com.nookure.staff.database.repository.SQLStaffStateRepository; import com.nookure.staff.messaging.NoneEventManager; import com.nookure.staff.messaging.RedisMessenger; +import com.nookure.staff.messaging.sql.SQLMessenger; import com.nookure.staff.paper.PaperPlayerWrapper; import com.nookure.staff.paper.StaffPaperPlayerWrapper; import com.nookure.staff.paper.addon.ServerAddonManager; import com.nookure.staff.paper.command.PaperCommandManager; +import com.nookure.staff.paper.factory.CustomCommandItemFactory; import com.nookure.staff.paper.factory.PaperPlayerWrapperFactory; import com.nookure.staff.paper.factory.StaffPaperPlayerWrapperFactory; +import com.nookure.staff.paper.hook.permission.DummyPermissionHook; +import com.nookure.staff.paper.hook.permission.LuckPermsPermissionHook; +import com.nookure.staff.paper.item.CustomCommandItem; import com.nookure.staff.paper.messaging.BackendMessageMessenger; import com.nookure.staff.paper.util.MockScheduler; +import com.nookure.staff.paper.util.transoformer.PaperPlayerTransformer; import com.nookure.staff.paper.util.PaperScheduler; import com.nookure.staff.paper.util.PaperServerUtils; +import com.nookure.staff.paper.util.transoformer.nametag.DummyNameTagTransformer; +import com.nookure.staff.paper.util.transoformer.nametag.TabNameTagTransformer; import com.nookure.staff.service.PinUserServiceImpl; import com.nookure.staff.service.UserNoteServiceImpl; import io.ebean.Database; -import io.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes; import org.bukkit.Bukkit; import org.bukkit.command.CommandMap; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; -import redis.clients.jedis.Jedis; +import org.jetbrains.annotations.NotNull; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; import javax.sql.DataSource; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; public class PaperPluginModule extends PluginModule { private final StaffBootstrapper boot; private MessengerConfig.MessengerType messengerType; private RedisPartial redisPartial; + private ConfigurationContainer glowConfig; public PaperPluginModule(StaffBootstrapper boot) { this.boot = boot; @@ -93,6 +105,7 @@ protected void configure() { bind(StaffPlayerExtensionManager.class).asEagerSingleton(); bind(FreezeManager.class).asEagerSingleton(); bind(PlaceholderManager.class).asEagerSingleton(); + bind(PlayerTransformer.class).to(PaperPlayerTransformer.class).asEagerSingleton(); bind(AddonManager.class) .to(ServerAddonManager.class) @@ -120,6 +133,11 @@ protected void configure() { .build(StaffPaperPlayerWrapperFactory.class) ); + install(new FactoryModuleBuilder() + .implement(CustomCommandItem.class, CustomCommandItem.class) + .build(CustomCommandItemFactory.class) + ); + try { /* * Configuration related area @@ -138,6 +156,8 @@ protected void configure() { }).toInstance(loadNoteMessages()); bind(new TypeLiteral>() { }).toInstance(loadStaffModeBlockedCommands()); + bind(new TypeLiteral>() { + }).toInstance(loadGlowConfig()); /* * PlayerWrapperManager related area @@ -146,6 +166,8 @@ protected void configure() { }).toInstance(playerWrapperManager); bind(new TypeLiteral>() { }).toInstance(playerWrapperManager); + bind(new TypeLiteral>>() { + }).toInstance(new ArrayList<>()); /* * AtomicReference related area @@ -164,11 +186,11 @@ protected void configure() { switch (messengerType) { case PM -> bind(EventMessenger.class).to(BackendMessageMessenger.class).asEagerSingleton(); case REDIS -> { - bind(Jedis.class).annotatedWith(RedisPublish.class).toInstance(getJedis()); - bind(Jedis.class).annotatedWith(RedisSubscribe.class).toInstance(getJedis()); + bind(JedisPool.class).toInstance(getJedisPool()); boot.getPLogger().info("Successfully connected to Redis"); bind(EventMessenger.class).to(RedisMessenger.class).asEagerSingleton(); } + case MYSQL -> bind(EventMessenger.class).to(SQLMessenger.class).asEagerSingleton(); case NONE -> bind(EventMessenger.class).to(NoneEventManager.class).asEagerSingleton(); default -> throw new RuntimeException("Unknown messenger type"); } @@ -177,6 +199,15 @@ protected void configure() { bind(EventMessenger.class).annotatedWith(PluginMessageMessenger.class).to(BackendMessageMessenger.class).asEagerSingleton(); else bind(EventMessenger.class).annotatedWith(PluginMessageMessenger.class).to(NoneEventManager.class).asEagerSingleton(); + + loadNameTagTransformer(); + + try { + Class.forName("net.luckperms.api.LuckPerms"); + bind(PermissionHook.class).to(LuckPermsPermissionHook.class).asEagerSingleton(); + } catch (ClassNotFoundException e) { + bind(PermissionHook.class).to(DummyPermissionHook.class).asEagerSingleton(); + } } private CommandMap getCommandMap() { @@ -205,22 +236,56 @@ private ConfigurationContainer loadCommands() throws IOException return ConfigurationContainer.load(boot.getDataFolder().toPath(), CommandConfig.class, "commands.yml"); } - private Jedis getJedis() { - try (Jedis jedis = new Jedis(redisPartial.getAddress(), redisPartial.getPort(), redisPartial.getTimeout(), redisPartial.getPoolSize())) { - if (!redisPartial.getPassword().isEmpty()) { - if (redisPartial.getUsername().isEmpty()) { - jedis.auth(redisPartial.getPassword()); - } else { - jedis.auth(redisPartial.getUsername(), redisPartial.getPassword()); - } - } + private ConfigurationContainer loadGlowConfig() throws IOException { + final var config = ConfigurationContainer.load(boot.getDataFolder().toPath(), GlowConfig.class, "glow.yml"); + this.glowConfig = config; + return config; + } + + private JedisPool getJedisPool() { + return getJedisPool( + redisPartial.getAddress(), + redisPartial.getPort(), + redisPartial.getTimeout(), + redisPartial.getPoolSize(), + redisPartial.getDatabase(), + redisPartial.getUsername(), + redisPartial.getPassword() + ); + } - jedis.select(redisPartial.getDatabase()); + private JedisPool getJedisPool( + @NotNull final String address, + final int port, + final int timeout, + final int poolSize, + final int database, + @NotNull final String username, + @NotNull final String password + ) { + JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxTotal(poolSize); - return jedis; - } catch (Exception e) { - boot.getPLogger().severe("Could not connect to Redis"); - throw new RuntimeException(e); + if (username.isEmpty() && password.isEmpty()) { + return new JedisPool(poolConfig, address, port, timeout, null, database); + } else if (username.isEmpty()) { + return new JedisPool(poolConfig, address, port, timeout, password, database); + } else { + return new JedisPool(poolConfig, address, port, timeout, username, password, database); + } + } + + private void loadNameTagTransformer() { + if (!glowConfig.get().tabIntegration) { + bind(NameTagTransformer.class).to(DummyNameTagTransformer.class).asEagerSingleton(); + return; + } + + try { + Class.forName("me.neznamy.tab.api.TabAPI"); + bind(NameTagTransformer.class).to(TabNameTagTransformer.class).asEagerSingleton(); + } catch (ClassNotFoundException e) { + bind(NameTagTransformer.class).to(DummyNameTagTransformer.class).asEagerSingleton(); } } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeChatCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeChatCommand.java index 3344797d5..b04581eba 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeChatCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeChatCommand.java @@ -10,6 +10,7 @@ import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.manager.FreezeManager; import com.nookure.staff.api.manager.PlayerWrapperManager; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.api.util.ServerUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -24,14 +25,25 @@ usage = "/freezechat " ) public class FreezeChatCommand extends StaffCommand { + private final ConfigurationContainer messages; + private final PlayerWrapperManager playerWrapperManager; + private final FreezeManager freezeManager; + private final ServerUtils serverUtils; + @Inject - private ConfigurationContainer messages; - @Inject - private PlayerWrapperManager playerWrapperManager; - @Inject - private FreezeManager freezeManager; - @Inject - private ServerUtils serverUtils; + public FreezeChatCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final PlayerWrapperManager playerWrapperManager, + @NotNull final FreezeManager freezeManager, + @NotNull final ServerUtils serverUtils + ) { + super(transformer, messages); + this.playerWrapperManager = playerWrapperManager; + this.freezeManager = freezeManager; + this.serverUtils = serverUtils; + this.messages = messages; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeCommand.java index 1c5f88a82..d15b790a4 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/FreezeCommand.java @@ -13,6 +13,7 @@ import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.manager.FreezeManager; import com.nookure.staff.api.manager.PlayerWrapperManager; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.paper.extension.FreezePlayerExtension; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -29,16 +30,28 @@ permission = Permissions.STAFF_FREEZE ) public class FreezeCommand extends StaffCommand { + private final PlayerWrapperManager playerWrapperManager; + private final ConfigurationContainer messages; + private final FreezeManager freezeManager; + private final ConfigurationContainer config; + private final Logger logger; + @Inject - private PlayerWrapperManager playerWrapperManager; - @Inject - private ConfigurationContainer messages; - @Inject - private FreezeManager freezeManager; - @Inject - private ConfigurationContainer config; - @Inject - private Logger logger; + public FreezeCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final PlayerWrapperManager playerWrapperManager, + @NotNull final FreezeManager freezeManager, + @NotNull final ConfigurationContainer config, + @NotNull final Logger logger + ) { + super(transformer, messages); + this.playerWrapperManager = playerWrapperManager; + this.messages = messages; + this.freezeManager = freezeManager; + this.config = config; + this.logger = logger; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { @@ -100,6 +113,7 @@ protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull Strin } if (!freezeManager.isFrozen(offlinePlayer.getUniqueId())) { + sender.sendMiniMessage(messages.get().freeze.playerNotFrozen()); return; } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/StaffChatCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/StaffChatCommand.java index 29389d50c..b99f340de 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/StaffChatCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/StaffChatCommand.java @@ -8,6 +8,7 @@ import com.nookure.staff.api.config.ConfigurationContainer; import com.nookure.staff.api.config.bukkit.BukkitConfig; import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.api.util.ServerUtils; import org.jetbrains.annotations.NotNull; @@ -21,12 +22,22 @@ description = "Send a message to all staff members" ) public class StaffChatCommand extends StaffCommand { + private final ServerUtils serverUtils; + private final ConfigurationContainer messages; + private final ConfigurationContainer config; + @Inject - private ServerUtils serverUtils; - @Inject - private ConfigurationContainer messages; - @Inject - private ConfigurationContainer config; + public StaffChatCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final ServerUtils serverUtils, + @NotNull final ConfigurationContainer config + ) { + super(transformer, messages); + this.serverUtils = serverUtils; + this.messages = messages; + this.config = config; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/VanishCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/VanishCommand.java index 077ccf5e9..a8cf34210 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/VanishCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/VanishCommand.java @@ -1,8 +1,12 @@ package com.nookure.staff.paper.command; + import com.google.inject.Inject; import com.nookure.staff.api.StaffPlayerWrapper; import com.nookure.staff.api.command.CommandData; import com.nookure.staff.api.command.StaffCommand; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -15,6 +19,11 @@ description = "Toggle vanish mode" ) public class VanishCommand extends StaffCommand { + @Inject + public VanishCommand(@NotNull final PlayerTransformer transformer, @NotNull final ConfigurationContainer messages) { + super(transformer, messages); + } + @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { sender.toggleVanish(); diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffCommandParent.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffCommandParent.java index a309da06b..119798d96 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffCommandParent.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffCommandParent.java @@ -1,11 +1,16 @@ package com.nookure.staff.paper.command.staff; +import com.google.inject.Inject; import com.nookure.staff.api.Permissions; +import com.nookure.staff.api.PlayerWrapper; import com.nookure.staff.api.StaffPlayerWrapper; import com.nookure.staff.api.command.Command; import com.nookure.staff.api.command.CommandData; import com.nookure.staff.api.command.CommandParent; import com.nookure.staff.api.command.CommandSender; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import org.jetbrains.annotations.NotNull; import java.lang.annotation.Annotation; @@ -22,6 +27,15 @@ } ) public class StaffCommandParent extends CommandParent { + private final PlayerTransformer transformer; + private final ConfigurationContainer messages; + + @Inject + public StaffCommandParent(@NotNull final PlayerTransformer transformer, @NotNull final ConfigurationContainer messages) { + this.transformer = transformer; + this.messages = messages; + } + @Override public void onCommand(@NotNull CommandSender sender, @NotNull String label, @NotNull List args) { if (args.isEmpty()) { @@ -30,7 +44,13 @@ public void onCommand(@NotNull CommandSender sender, @NotNull String label, @Not return; } - sender.sendMiniMessage("Only staff members can execute this command."); + if (sender.hasPermission("nookure.staff") && sender instanceof PlayerWrapper player) { + transformer.player2Staff(player.getUniqueId()); + player.sendMiniMessage(messages.get().youAreNowAnStaff()); + return; + } + + sender.sendMiniMessage(messages.get().onlyStaffMembersCanExecuteThisCommand()); return; } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffEnderchestSee.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffEnderchestSee.java index e31317334..91dbed49b 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffEnderchestSee.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffEnderchestSee.java @@ -7,6 +7,7 @@ import com.nookure.staff.api.command.StaffCommand; import com.nookure.staff.api.config.ConfigurationContainer; import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.paper.StaffPaperPlayerWrapper; import com.nookure.staff.paper.inventory.player.PlayerEnderchestInventory; import org.bukkit.Bukkit; @@ -21,8 +22,16 @@ permission = Permissions.STAFF_ENDERCHEST ) public class StaffEnderchestSee extends StaffCommand { + private final ConfigurationContainer messages; + @Inject - private ConfigurationContainer messages; + public StaffEnderchestSee( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages + ) { + super(transformer, messages); + this.messages = messages; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffInvSee.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffInvSee.java index 259e620be..a26c59222 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffInvSee.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffInvSee.java @@ -7,6 +7,7 @@ import com.nookure.staff.api.command.StaffCommand; import com.nookure.staff.api.config.ConfigurationContainer; import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.paper.StaffPaperPlayerWrapper; import com.nookure.staff.paper.inventory.player.PlayerInventory; import org.bukkit.Bukkit; @@ -21,8 +22,16 @@ permission = Permissions.STAFF_INVSEE ) public class StaffInvSee extends StaffCommand { + private final ConfigurationContainer messages; + @Inject - private ConfigurationContainer messages; + public StaffInvSee( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages + ) { + super(transformer, messages); + this.messages = messages; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffListCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffListCommand.java index 5e3c89bce..883417e42 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffListCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/command/staff/StaffListCommand.java @@ -5,7 +5,10 @@ import com.nookure.staff.api.StaffPlayerWrapper; import com.nookure.staff.api.command.CommandData; import com.nookure.staff.api.command.StaffCommand; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.manager.PlayerWrapperManager; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.paper.StaffPaperPlayerWrapper; import com.nookure.staff.paper.inventory.InventoryList; import org.bukkit.entity.Player; @@ -20,10 +23,20 @@ permission = "nookurestaff.staff.list" ) public class StaffListCommand extends StaffCommand { + private final AtomicReference engine; + private final PlayerWrapperManager playerWrapperManager; + @Inject - private AtomicReference engine; - @Inject - private PlayerWrapperManager playerWrapperManager; + public StaffListCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final AtomicReference engine, + @NotNull final PlayerWrapperManager playerWrapperManager + ) { + super(transformer, messages); + this.engine = engine; + this.playerWrapperManager = playerWrapperManager; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/extension/staff/PaperGlowPlayerExtension.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/extension/staff/PaperGlowPlayerExtension.java new file mode 100644 index 000000000..026d77551 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/extension/staff/PaperGlowPlayerExtension.java @@ -0,0 +1,53 @@ + package com.nookure.staff.paper.extension.staff; + +import com.google.inject.Inject; +import com.nookure.staff.api.StaffPlayerWrapper; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.GlowConfig; +import com.nookure.staff.api.extension.staff.GlowPlayerExtension; +import com.nookure.staff.api.hook.PermissionHook; +import com.nookure.staff.api.util.transformer.NameTagTransformer; +import com.nookure.staff.paper.StaffPaperPlayerWrapper; +import org.jetbrains.annotations.NotNull; + +public class PaperGlowPlayerExtension extends GlowPlayerExtension { + private final StaffPaperPlayerWrapper player; + private final ConfigurationContainer config; + private final NameTagTransformer nameTagTransformer; + private final PermissionHook permissionHook; + + @Inject + public PaperGlowPlayerExtension( + @NotNull final StaffPlayerWrapper player, + @NotNull final ConfigurationContainer config, + @NotNull final NameTagTransformer nameTagTransformer, + @NotNull final PermissionHook permissionHook + ) { + super(player); + + this.player = (StaffPaperPlayerWrapper) player; + this.config = config; + this.nameTagTransformer = nameTagTransformer; + this.permissionHook = permissionHook; + } + + @Override + public void onStaffModeEnabled() { + final String color = getGlowColor(); + nameTagTransformer.setPrefix(player, color); + player.getPlayer().setGlowing(true); + } + + @Override + public void onStaffModeDisabled() { + nameTagTransformer.removePrefix(player); + player.getPlayer().setGlowing(false); + } + + @Override + public @NotNull String getGlowColor() { + final String color = config.get().glowColors.get(permissionHook.getHighestGroup(player)); + + return color == null ? config.get().defaultColor : color; + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/factory/CustomCommandItemFactory.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/factory/CustomCommandItemFactory.java new file mode 100644 index 000000000..7f57a8998 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/factory/CustomCommandItemFactory.java @@ -0,0 +1,9 @@ +package com.nookure.staff.paper.factory; + +import com.nookure.staff.api.config.bukkit.partials.CustomItemPartial; +import com.nookure.staff.paper.item.CustomCommandItem; +import org.jetbrains.annotations.NotNull; + +public interface CustomCommandItemFactory { + @NotNull CustomCommandItem create(@NotNull final CustomItemPartial customItemPartial); +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/DummyPermissionHook.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/DummyPermissionHook.java new file mode 100644 index 000000000..3bc41c3dd --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/DummyPermissionHook.java @@ -0,0 +1,12 @@ +package com.nookure.staff.paper.hook.permission; + +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.hook.PermissionHook; +import org.jetbrains.annotations.NotNull; + +public final class DummyPermissionHook implements PermissionHook { + @Override + public @NotNull String getHighestGroup(@NotNull PlayerWrapper player) { + return "default"; + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/LuckPermsPermissionHook.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/LuckPermsPermissionHook.java new file mode 100644 index 000000000..5a6aae0ba --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/hook/permission/LuckPermsPermissionHook.java @@ -0,0 +1,25 @@ +package com.nookure.staff.paper.hook.permission; + +import com.google.inject.Singleton; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.hook.PermissionHook; +import net.luckperms.api.LuckPerms; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +@Singleton +public final class LuckPermsPermissionHook implements PermissionHook { + private final RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + + @Override + public @NotNull String getHighestGroup(@NotNull PlayerWrapper player) { + if (provider == null) { + throw new IllegalStateException("LuckPerms is not installed on the server."); + } + + return requireNonNull(provider.getProvider().getUserManager().getUser(player.getUniqueId())).getPrimaryGroup(); + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/item/CustomCommandItem.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/item/CustomCommandItem.java new file mode 100644 index 000000000..593a7aa3b --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/item/CustomCommandItem.java @@ -0,0 +1,78 @@ +package com.nookure.staff.paper.item; + +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.config.bukkit.partials.CustomItemPartial; +import com.nookure.staff.api.config.bukkit.partials.CustomItemType; +import com.nookure.staff.api.item.ExecutableItem; +import com.nookure.staff.api.item.PlayerInteractItem; +import com.nookure.staff.api.item.StaffItem; +import com.nookure.staff.paper.StaffPaperPlayerWrapper; +import org.bukkit.Bukkit; + +import org.jetbrains.annotations.NotNull; + +public class CustomCommandItem extends StaffItem implements ExecutableItem, PlayerInteractItem { + private final CustomItemPartial customItemPartial; + + @Inject + public CustomCommandItem(@Assisted CustomItemPartial customItemPartial) { + super(customItemPartial); + this.customItemPartial = customItemPartial; + } + + @Override + public void click(@NotNull PlayerWrapper player) { + if (customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_PLAYER || customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_CONSOLE) { + return; + } + + if (!(player instanceof StaffPaperPlayerWrapper staff)) { + return; + } + + if (customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_PLAYER) { + Bukkit.dispatchCommand(staff.getPlayer(), customItemPartial.getCommand() + .replace("{player}", staff.getName()) + .replace("{player_uuid}", staff.getUniqueId().toString()) + ); + } + + if (customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_CONSOLE) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), customItemPartial.getCommand() + .replace("{player}", staff.getName()) + .replace("{player_uuid}", staff.getUniqueId().toString()) + ); + } + } + + @Override + public void click(@NotNull PlayerWrapper player, @NotNull PlayerWrapper target) { + if (customItemPartial.getType() == CustomItemType.COMMAND_AS_CONSOLE || customItemPartial.getType() == CustomItemType.COMMAND_AS_PLAYER) { + return; + } + + if (!(player instanceof StaffPaperPlayerWrapper staff)) { + return; + } + + if (customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_PLAYER) { + staff.getPlayer().performCommand(customItemPartial.getCommand() + .replace("{player}", staff.getName()) + .replace("{player_uuid}", staff.getUniqueId().toString()) + .replace("{target}", target.getName()) + .replace("{target_uuid}", target.getUniqueId().toString()) + ); + } + + if (customItemPartial.getType() == CustomItemType.COMMAND_TARGET_AS_CONSOLE) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), customItemPartial.getCommand() + .replace("{player}", staff.getName()) + .replace("{player_uuid}", staff.getUniqueId().toString()) + .replace("{target}", target.getName()) + .replace("{target_uuid}", target.getUniqueId().toString()) + ); + } + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/listener/OnPlayerJoin.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/listener/OnPlayerJoin.java index 0f371ad3b..3e39f32a6 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/listener/OnPlayerJoin.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/listener/OnPlayerJoin.java @@ -1,6 +1,7 @@ package com.nookure.staff.paper.listener; import com.google.inject.Inject; +import com.google.inject.Singleton; import com.nookure.staff.api.Logger; import com.nookure.staff.api.Permissions; import com.nookure.staff.api.config.ConfigurationContainer; @@ -19,7 +20,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.List; public class OnPlayerJoin implements Listener { @@ -27,12 +27,13 @@ public class OnPlayerJoin implements Listener { private final Logger logger; private final PaperPlayerWrapperFactory playerWrapperFactory; private final StaffPaperPlayerWrapperFactory staffPaperPlayerWrapperFactory; - private final List> states = new ArrayList<>(); + private final List> states; @Inject public OnPlayerJoin( @NotNull final PlayerWrapperManager playerWrapperManager, @NotNull final Logger logger, + @NotNull final List> states, @NotNull final PaperPlayerWrapperFactory playerWrapperFactory, @NotNull final StaffPaperPlayerWrapperFactory staffPaperPlayerWrapperFactory, @NotNull final ConfigurationContainer config @@ -41,6 +42,7 @@ public OnPlayerJoin( this.logger = logger; this.playerWrapperFactory = playerWrapperFactory; this.staffPaperPlayerWrapperFactory = staffPaperPlayerWrapperFactory; + this.states = states; if (config.get().modules.isPinCode()) { states.add(PinState.class); diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/ItemsLoader.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/ItemsLoader.java index e4505298b..cf5faeaf2 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/ItemsLoader.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/ItemsLoader.java @@ -8,6 +8,7 @@ import com.nookure.staff.api.item.Items; import com.nookure.staff.api.manager.StaffItemsManager; import com.nookure.staff.api.util.AbstractLoader; +import com.nookure.staff.paper.factory.CustomCommandItemFactory; import com.nookure.staff.paper.item.*; public class ItemsLoader implements AbstractLoader { @@ -19,6 +20,8 @@ public class ItemsLoader implements AbstractLoader { private Injector injector; @Inject private Logger logger; + @Inject + private CustomCommandItemFactory customCommandItemFactory; @Override public void load() { @@ -39,5 +42,25 @@ public void load() { default -> logger.severe("Could not find %s item class", name); } }); + + itemConfig.get().staffItems.getCustomItems().forEach((name, item) -> { + if (!item.isEnabled()) return; + if (item.getCommand() == null) { + logger.severe("Could not find %s item command", name); + return; + } + + if (item.getType() == null) { + logger.severe("Could not find %s item type", name); + return; + } + + manager.addItem(name, customCommandItemFactory.create(item)); + }); + } + + @Override + public void reload() { + load(); } } diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/SQLPollTaskLoader.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/SQLPollTaskLoader.java new file mode 100644 index 000000000..1331df7eb --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/loader/SQLPollTaskLoader.java @@ -0,0 +1,27 @@ +package com.nookure.staff.paper.loader; + +import com.google.inject.Inject; +import com.nookure.staff.messaging.sql.SQLPollTask; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +import java.io.Closeable; + +public class SQLPollTaskLoader implements Closeable { + private final BukkitTask pollTask; + private final BukkitTask cleanupTask; + + @Inject + public SQLPollTaskLoader(@NotNull final SQLPollTask sqlPollTask, @NotNull final JavaPlugin bootstrapper) { + pollTask = Bukkit.getScheduler().runTaskTimerAsynchronously(bootstrapper, sqlPollTask::pollMessages, 0, 20); + cleanupTask = Bukkit.getScheduler().runTaskTimerAsynchronously(bootstrapper, sqlPollTask::cleanup, 0, 60 * 20); + } + + @Override + public void close() { + pollTask.cancel(); + cleanupTask.cancel(); + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/permission/LuckPermsPermissionInterceptor.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/permission/LuckPermsPermissionInterceptor.java new file mode 100644 index 000000000..5ace39757 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/permission/LuckPermsPermissionInterceptor.java @@ -0,0 +1,92 @@ +package com.nookure.staff.paper.permission; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.nookure.staff.api.util.transformer.PlayerTransformer; +import com.nookure.staff.paper.bootstrap.StaffBootstrapper; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.event.EventSubscription; +import net.luckperms.api.event.node.NodeAddEvent; +import net.luckperms.api.model.group.Group; +import net.luckperms.api.model.user.User; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.jetbrains.annotations.NotNull; + +import java.io.Closeable; +import java.util.Optional; + +@Singleton +public final class LuckPermsPermissionInterceptor implements Closeable { + private final EventSubscription nodeAddEventEventSubscription; + private final PlayerTransformer playerTransformer; + + private static final String BASE_PERMISSION = "nookure.staff"; + private static final String GROUP_PERMISSION = "group."; + + @Inject + public LuckPermsPermissionInterceptor( + @NotNull final StaffBootstrapper bootstrapper, + @NotNull final PlayerTransformer playerTransformer + ) { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + + if (provider == null) { + throw new IllegalStateException("LuckPerms is not installed on the server."); + } + + LuckPerms luckPerms = provider.getProvider(); + + this.playerTransformer = playerTransformer; + + nodeAddEventEventSubscription = luckPerms.getEventBus().subscribe(bootstrapper, NodeAddEvent.class, this::onNodeAdd); + } + + private void onNodeAdd(@NotNull final NodeAddEvent event) { + if (nookureStaffBasePermissionCheck(event)) return; + if (fullPermissionGrant(event)) return; + if (playerAddedToGroupCheck(event)) return; + } + + public boolean fullPermissionGrant(@NotNull final NodeAddEvent event) { + if (!event.getNode().getKey().equals("*")) return false; + if (!(event.getTarget() instanceof User user)) return false; + if (!event.getNode().getValue()) playerTransformer.staff2player(user.getUniqueId()); + + playerTransformer.player2Staff(user.getUniqueId()); + return true; + } + + public boolean nookureStaffBasePermissionCheck(@NotNull final NodeAddEvent event) { + if (!event.getNode().getKey().equals(BASE_PERMISSION)) return false; + if (!(event.getTarget() instanceof User user)) return false; + if (!event.getNode().getValue()) playerTransformer.staff2player(user.getUniqueId()); + else playerTransformer.player2Staff(user.getUniqueId()); + return true; + } + + public boolean playerAddedToGroupCheck(@NotNull final NodeAddEvent event) { + if (!event.getNode().getKey().startsWith(GROUP_PERMISSION)) return false; + if (!(event.getTarget() instanceof User user)) return false; + if (!event.getNode().getValue()) return false; + + Optional group = user.getInheritedGroups(user.getQueryOptions()) + .stream() + .filter(g -> g.getName().equals(event.getNode().getKey().substring(GROUP_PERMISSION.length()))) + .findFirst(); + + if (group.isEmpty()) return false; + + if (group.get().getNodes().stream().anyMatch(node -> node.getKey().equals(BASE_PERMISSION))) { + playerTransformer.player2Staff(user.getUniqueId()); + return true; + } + + return false; + } + + @Override + public void close() { + nodeAddEventEventSubscription.close(); + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/ChangePin.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/ChangePin.java index 4bbef1090..e0fd21a30 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/ChangePin.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/ChangePin.java @@ -10,6 +10,7 @@ import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.service.PinUserService; import com.nookure.staff.api.state.PinState; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -20,10 +21,19 @@ description = "Change your pin" ) public class ChangePin extends StaffCommand { + private final ConfigurationContainer messages; + private final PinUserService service; + @Inject - private ConfigurationContainer messages; - @Inject - private PinUserService service; + public ChangePin( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final PinUserService service + ) { + super(transformer, messages); + this.messages = messages; + this.service = service; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/DeletePinCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/DeletePinCommand.java index 68d73a177..cc861acc1 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/DeletePinCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/DeletePinCommand.java @@ -9,6 +9,7 @@ import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.model.PinModel; import com.nookure.staff.api.model.PlayerModel; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import io.ebean.Database; import org.jetbrains.annotations.NotNull; @@ -21,10 +22,19 @@ description = "Delete your pin" ) public class DeletePinCommand extends StaffCommand { + private final AtomicReference database; + private final ConfigurationContainer messages; + @Inject - private AtomicReference database; - @Inject - private ConfigurationContainer messages; + public DeletePinCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final AtomicReference database + ) { + super(transformer, messages); + this.database = database; + this.messages = messages; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/SetPinCommand.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/SetPinCommand.java index 19a55a581..f9555e2f7 100644 --- a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/SetPinCommand.java +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/pin/command/SetPinCommand.java @@ -4,13 +4,12 @@ import com.nookure.core.inv.paper.PaperNookureInventoryEngine; import com.nookure.staff.api.Permissions; import com.nookure.staff.api.StaffPlayerWrapper; -import com.nookure.staff.api.command.Command; import com.nookure.staff.api.command.CommandData; -import com.nookure.staff.api.command.CommandSender; import com.nookure.staff.api.command.StaffCommand; import com.nookure.staff.api.config.ConfigurationContainer; import com.nookure.staff.api.config.bukkit.BukkitMessages; import com.nookure.staff.api.state.PinState; +import com.nookure.staff.api.util.transformer.PlayerTransformer; import com.nookure.staff.paper.StaffPaperPlayerWrapper; import com.nookure.staff.paper.inventory.InventoryList; import org.jetbrains.annotations.NotNull; @@ -24,10 +23,19 @@ description = "Set your pin" ) public class SetPinCommand extends StaffCommand { + private final AtomicReference engine; + private final ConfigurationContainer messages; + @Inject - private AtomicReference engine; - @Inject - private ConfigurationContainer messages; + public SetPinCommand( + @NotNull final PlayerTransformer transformer, + @NotNull final ConfigurationContainer messages, + @NotNull final AtomicReference engine + ) { + super(transformer, messages); + this.engine = engine; + this.messages = messages; + } @Override protected void onStaffCommand(@NotNull StaffPlayerWrapper sender, @NotNull String label, @NotNull List args) { diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/PaperPlayerTransformer.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/PaperPlayerTransformer.java new file mode 100644 index 000000000..acb0401e5 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/PaperPlayerTransformer.java @@ -0,0 +1,98 @@ +package com.nookure.staff.paper.util.transoformer; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.nookure.staff.api.Logger; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.StaffPlayerWrapper; +import com.nookure.staff.api.config.ConfigurationContainer; +import com.nookure.staff.api.config.bukkit.BukkitMessages; +import com.nookure.staff.api.manager.FreezeManager; +import com.nookure.staff.api.manager.PlayerWrapperManager; +import com.nookure.staff.api.state.PlayerState; +import com.nookure.staff.api.util.transformer.PlayerTransformer; +import com.nookure.staff.paper.PaperPlayerWrapper; +import com.nookure.staff.paper.StaffPaperPlayerWrapper; +import com.nookure.staff.paper.extension.FreezePlayerExtension; +import com.nookure.staff.paper.factory.PaperPlayerWrapperFactory; +import com.nookure.staff.paper.factory.StaffPaperPlayerWrapperFactory; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.UUID; + +@Singleton +public final class PaperPlayerTransformer implements PlayerTransformer { + private final PlayerWrapperManager playerWrapperManager; + private final PaperPlayerWrapperFactory paperPlayerWrapperFactory; + private final StaffPaperPlayerWrapperFactory staffPaperPlayerWrapperFactory; + private final Logger logger; + private final ConfigurationContainer messages; + private final FreezeManager freezeManager; + private final List> states; + + @Inject + public PaperPlayerTransformer( + @NotNull final PlayerWrapperManager playerWrapperManager, + @NotNull final PaperPlayerWrapperFactory paperPlayerWrapperFactory, + @NotNull final StaffPaperPlayerWrapperFactory staffPaperPlayerWrapperFactory, + @NotNull final Logger logger, + @NotNull final ConfigurationContainer messages, + @NotNull final FreezeManager freezeManager, + @NotNull final List> states + ) { + this.playerWrapperManager = playerWrapperManager; + this.paperPlayerWrapperFactory = paperPlayerWrapperFactory; + this.staffPaperPlayerWrapperFactory = staffPaperPlayerWrapperFactory; + this.freezeManager = freezeManager; + this.logger = logger; + this.messages = messages; + this.states = states; + } + + @Override + public void player2Staff(@NotNull final UUID uuid) { + final PlayerWrapper player = playerWrapperManager.getPlayerWrapperOrNull(uuid); + if (player == null) return; + + if (player instanceof StaffPlayerWrapper) { + logger.debug("Player %s is already a staff member.", player.getName()); + } + + if (!(player instanceof PaperPlayerWrapper paperPlayerWrapper)) return; + + playerWrapperManager.removePlayerWrapper(paperPlayerWrapper.getPlayer()); + final StaffPaperPlayerWrapper staffPlayerWrapper = staffPaperPlayerWrapperFactory + .create(paperPlayerWrapper.getPlayer(), states); + + playerWrapperManager.addPlayerWrapper(staffPlayerWrapper.getPlayer(), staffPlayerWrapper); + staffPlayerWrapper.sendMiniMessage(messages.get().youAreNowAnStaffDuringPermissionInterceptor()); + } + + @Override + public void staff2player(@NotNull final UUID uuid) { + final PlayerWrapper player = playerWrapperManager.getPlayerWrapperOrNull(uuid); + if (player == null) return; + + if (player instanceof PaperPlayerWrapper) { + logger.debug("Player %s is already a player.", player.getName()); + } + + if (!(player instanceof StaffPaperPlayerWrapper staffPlayerWrapper)) return; + + if (staffPlayerWrapper.isInStaffMode()) { + staffPlayerWrapper.disableVanish(false); + staffPlayerWrapper.disableStaffMode(); + staffPlayerWrapper.getExtension(FreezePlayerExtension.class).flatMap(extension -> freezeManager.stream().filter(freezeContainer -> freezeContainer.target().equals(staffPlayerWrapper.getPlayer().getUniqueId())) + .findFirst()).ifPresent(freezeContainer -> { + freezeManager.removeFreezeContainer(freezeContainer.target()); + }); + } + + playerWrapperManager.removePlayerWrapper(staffPlayerWrapper.getPlayer()); + final PaperPlayerWrapper paperPlayerWrapper = paperPlayerWrapperFactory.create(staffPlayerWrapper.getPlayer(), List.of()); + + playerWrapperManager.addPlayerWrapper(paperPlayerWrapper.getPlayer(), paperPlayerWrapper); + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/DummyNameTagTransformer.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/DummyNameTagTransformer.java new file mode 100644 index 000000000..d4cd166b2 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/DummyNameTagTransformer.java @@ -0,0 +1,29 @@ +package com.nookure.staff.paper.util.transoformer.nametag; + +import com.google.inject.Singleton; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.util.transformer.NameTagTransformer; +import org.jetbrains.annotations.NotNull; + +@Singleton +public class DummyNameTagTransformer implements NameTagTransformer { + @Override + public void setPrefix(@NotNull PlayerWrapper player, @NotNull String prefix) { + + } + + @Override + public void removePrefix(@NotNull PlayerWrapper player) { + + } + + @Override + public void setSuffix(@NotNull PlayerWrapper player, @NotNull String suffix) { + + } + + @Override + public void removeSuffix(@NotNull PlayerWrapper player) { + + } +} diff --git a/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/TabNameTagTransformer.java b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/TabNameTagTransformer.java new file mode 100644 index 000000000..88471e088 --- /dev/null +++ b/NookureStaff-Paper/src/main/java/com/nookure/staff/paper/util/transoformer/nametag/TabNameTagTransformer.java @@ -0,0 +1,62 @@ +package com.nookure.staff.paper.util.transoformer.nametag; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.nookure.staff.api.Logger; +import com.nookure.staff.api.PlayerWrapper; +import com.nookure.staff.api.util.transformer.NameTagTransformer; +import me.neznamy.tab.api.TabAPI; +import me.neznamy.tab.api.TabPlayer; +import me.neznamy.tab.api.nametag.NameTagManager; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +@Singleton +public final class TabNameTagTransformer implements NameTagTransformer { + private final NameTagManager nameTagManager = TabAPI.getInstance().getNameTagManager(); + + @Inject + public TabNameTagTransformer(@NotNull final Logger logger) { + if (nameTagManager == null) { + logger.severe("TabAPI is not loaded, disabling TabNametagTransformer."); + } + } + + @Override + public void setPrefix(@NotNull PlayerWrapper player, @NotNull String prefix) { + requireNonNull(player, "Player cannot be null."); + requireNonNull(prefix, "Prefix cannot be null."); + + if (nameTagManager == null) return; + nameTagManager.setPrefix(getTabPlayer(player), prefix); + } + + @Override + public void removePrefix(@NotNull PlayerWrapper player) { + requireNonNull(player, "Player cannot be null."); + + if (nameTagManager == null) return; + nameTagManager.setPrefix(getTabPlayer(player), null); + } + + @Override + public void setSuffix(@NotNull PlayerWrapper player, @NotNull String suffix) { + requireNonNull(player, "Player cannot be null."); + + if (nameTagManager == null) return; + nameTagManager.setSuffix(getTabPlayer(player), suffix); + } + + @Override + public void removeSuffix(@NotNull PlayerWrapper player) { + requireNonNull(player, "Player cannot be null."); + + if (nameTagManager == null) return; + nameTagManager.setSuffix(getTabPlayer(player), null); + } + + private TabPlayer getTabPlayer(PlayerWrapper player) { + return TabAPI.getInstance().getPlayer(player.getUniqueId()); + } +} diff --git a/README.md b/README.md index b20b61b81..662daf348 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,67 @@ +# Introducing NookureStaff OSS +> This is the free version of the plugin available to the public. It currently includes all the features of the formerly premium version. However, please note that personalized support through Discord tickets will only be available to buyers of NookureStaff. The free version might have limitations in the future and could require an additional payment for certain features. The plugin's source code will remain published under the AGPLv3 license. -![Nookure Staff](https://i.imgur.com/3ngZgnP.png) +NookureStaff is the most advanced staff utilities plugin for Minecraft servers. Its comprehensive features, extensive customization options, and remarkable flexibility make it suitable for any type of Minecraft server, whether a small standalone server or a massive multi-proxy network. -# Nookure Staff +## Main features of the plugin +Our plugin has an extensive list of features that can be chosen to be toggled on or off by the end user, but we always make every aspect configurable. -### Introducing NookureStaff (formerly MAStaff) -NookureStaff is the most advanced staff utilities plugin for Minecraft servers. Its comprehensive features, extensive customization options, and remarkable flexibility make it suitable for any type of Minecraft server, whether a small standalone server or a massive multi-proxy network. +## Player Actions +You can do shift + click on a player and you will have a full GUI with a lot of actions for the current player, you can add whatever you want to this inventory!, the only limit is your imagination! You can also open this inventory by performing the following command: /player-list +![https://i.imgur.com/cyAiY6w.gif](https://i.imgur.com/cyAiY6w.gif) + +## Staff Mode +This mode restricts staff members from impacting the server state unless they have special permissions, while still granting them access to valuable moderation tools. + +![https://i.imgur.com/y8GdBmv.png](https://i.imgur.com/y8GdBmv.png) + +## User Notes +This module allows adding notes to users on the Minecraft server. These notes can be displayed when the user logs in. Additionally, admins can set notes to be visible only to other admins, allowing them to leave notes for other staff members as well. + +![https://i.imgur.com/ak7qTkF.png](https://i.imgur.com/ak7qTkF.png) + +## Vanish +The vanish state can be activated by staff members using a command or while in Staff Mode, rendering them invisible to regular players. This feature enables staff to spectate players discreetly, monitor for hacks, and ensure compliance with server rules. + +## Freeze +This tool enables staff members to freeze players and display a message in the chat. It can be particularly useful when staff need to conduct a screenshare on a user to verify if they are cheating. +![https://i.imgur.com/J2QEQU7.png](https://i.imgur.com/J2QEQU7.png) +## **Staff Chat** +The Staff Chat feature provides a private communication channel for online staff members within the Minecraft server, allowing them to interact with each other discreetly without using the public chat. + +## Multi Server +This plugin is not only capable of running on an independent Minecraft server but is also compatible with Proxy synchronization. This compatibility enables simultaneous usage across multiple servers, facilitating communication of certain plugin features between the backend servers. + +## Multi Proxy +Nookure Staff is compatible with Proxy synchronization for multi-server use and seamlessly operates on multiple proxies with Redis integration. This allows efficient communication and synchronization of plugin functions across different proxy servers, enhancing scalability and performance in complex server setups. +![https://docs.nookure.com/assets/redis.BSTi5gb_.svg](https://docs.nookure.com/assets/redis.BSTi5gb_.svg) + +## Customization +Nookure Staff offers extensively customizable configuration files that allow for precise adjustments to every aspect of the plugin. Messages, items, and commands can be effortlessly modified within seconds. Additionally, the plugin boasts a variety of features that can be selectively enabled or disabled to tailor the user experience and ensure optimal server performance. + +## Multi-server and proxy sync +This plugin can work on multiple servers or proxies connecting each instance to a MySQL database. If you want, you can also setup Redis, following the steps in our [official documentation]('https://docs.nookure.com/nkstaff/messenger.html'). -# Installation -Download the plugin from an official source like [BuiltByBit](https://builtbybit.com/resources/nookurestaff-staffmode-utils.25460/) or [Polymart](https://polymart.org/resource/nookurestaff-staffmode-amp-utils.3051) +## Usage Stats +The plugin name in bstats appear as MAStaff (the old plugin name) +![https://bstats.org/signatures/bukkit/MAStaff.svg](https://bstats.org/signatures/bukkit/MAStaff.svg) -Then drag and drop the jar file to your server plugins config, to customize the plugin and its configs please visit the [documentation](https://docs.nookure.com/nkstaff/what-is-nookurestaff.html) +## Frequently Asked Questions +**Q**: Do I need to have more than one server to use this plugin? +**A: **No, this plugin can run on standalone servers or multiple servers at once, depending on your needs. -# Development -To build the project you need to have gradle and JDK 17 +**Q: **Do I need a license to use this plugin? +**A:** No. You only have to download the plugin from the website and drag it to your plugins folder on your server. -First you need to clone the repo +**Q: **Does this plugin support proxies? +**A:** Yes, the plugin supports both BungeeCord (and forks) and Velocity. We recommend Velocity for a better experience, as it is better in every aspect. -```shell -git clone https://github.com/Nookure/NookureStaff -``` +**Q: **How can I setup the plugin on my server? +**A:** You can check our [documentation]('https://docs.nookure.com/nkstaff/messenger.html') to learn how the plugin works and configure each aspect. If you find any difficulties during the process, you can join [our Discord server]('https://discord.nookure.com') and open a ticket, where we will help you fix your issue. -Then you need to build the project -```shell -gradlew shadowJar -``` +**Q:** I can't find a feature that I would like. How can I suggest that to be implemented? +**A: **You can join [our Discord server]('https://discord.nookure.com') and open a ticket explaining in detail what would you like to be implemented in next updates. We are always listening for feedback and trying to make NookureStaff better! -If you have modified a model or create one you will need to generate a migration file -```sh -gradlew NookureStaff-Common:generateMigration -``` \ No newline at end of file +## Important information +When buying NookureStaff, you will be able to ask for a copy of the source code on [our Discord server]('https://discord.nookure.com'). Open a ticket providing your proof of payment and your GitHub account email address, and you'll get access. For help setting up the plugin, you can check our official documentation. Here you will find everything you need to learn how to setup the plugin. If you want to report any issue, please contact us on [our Discord server]('https://discord.nookure.com') opening a ticket. We are also open to new suggestions and feature ideas, so feel free to give us your opinion about the plugin. To help other users on the website, we would appreciate you if you could leave a positive rating on the resource in case it's working well for you. Otherwise, we would like to help you, so we would appreciate if you could open a ticket on [our Discord server]('https://discord.nookure.com') in order to be able to help you. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 84404e7c8..7bc082e10 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.grgit) alias(libs.plugins.runPaper) alias(libs.plugins.testLogger) + alias(libs.plugins.minotaur) } val major: String by project @@ -18,6 +19,8 @@ val versionCode = "${major}.${minor}.${patch}" version = "${versionCode}-${grgit.head().abbreviatedId}" +val branch = grgit.branch.current().name + if (System.getenv("nookure_staff_version") != null) { version = "${versionCode}-${System.getenv("nookure_staff_version")}" } @@ -33,7 +36,14 @@ dependencies { } tasks.shadowJar { - archiveFileName.set("NookureStaff-${rootProject.version}.jar") + val dev = System.getenv("NOOKURE_STAFF_DEV") == "true" + + if (dev) { + archiveFileName.set("NookureStaff-dev.jar") + } else { + archiveFileName.set("NookureStaff-${rootProject.version}.jar") + } + dependencies { exclude("com/mojang") } @@ -99,10 +109,44 @@ tasks.withType(xyz.jpenilla.runtask.task.AbstractRun::class) { } jvmArgs("-XX:+AllowEnhancedClassRedefinition", "-XX:+AllowRedefinitionToAddDeleteMethods") systemProperties["nkstaff.inventory.replace"] = "true" + systemProperties["file.encoding"] = "UTF-8" } tasks { runServer { - minecraftVersion("1.21.3") + minecraftVersion("1.21.4") } +} + +modrinth { + token = System.getenv("MODRINTH_TOKEN") + projectId = "staff" + versionNumber.set(rootProject.version.toString()) + + versionType = if (branch.startsWith("release/")) { + "release" + } else if (branch.startsWith("dev")) { + "beta" + } else if (branch.startsWith("feature/") || branch.startsWith("fix/")) { + "alpha" + } else { + System.getenv("MODRINTH_VERSION_TYPE") ?: "release" + } + val changeLog = getChangeLog(); + System.out.println(changeLog) + changelog.set(changeLog) + uploadFile.set(tasks.shadowJar.get().archiveFile) + gameVersions.addAll("1.19.4", "1.20.6", "1.21", "1.21.1", "1.21.2", "1.21.3", "1.21.4") + loaders.addAll("paper", "purpur", "velocity") + + syncBodyFrom = rootProject.file("README.md").readText() +} + +fun getChangeLog(): String { + return "${grgit.head().shortMessage} - ${grgit.head().author.name} (${grgit.head().abbreviatedId})" +} + +tasks.modrinth { + dependsOn(tasks.shadowJar) + dependsOn(tasks.modrinthSyncBody) } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 30f9c6910..20d632e4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ major=1 -minor=4 -patch=5 \ No newline at end of file +minor=5 +patch=0 + +# Gradle settings +org.gradle.jvmargs=-Dfile.encoding=UTF-8 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 225aea79f..b5eb1e5de 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,7 @@ [versions] -configManager = "1.4" -hikariCP = "4.0.3" +hikariCP = "6.2.1" invApiVer = "4.1.10" -gson = "2.10.1" +gson = "2.11.0" libly = "2.0.0-SNAPSHOT" reflections = "0.10.2" paperLib = "1.0.7" @@ -11,20 +10,20 @@ paper = "1.21-R0.1-SNAPSHOT" vault = "1.7.1" placeholderApi = "2.11.6" bungeecord = "1.21-R0.1-SNAPSHOT" -jedis = "4.4.0-m2" -adventure = "4.16.0" -adventurePlatform = "4.3.0" +jedis = "5.2.0" +adventure = "4.17.0" +adventurePlatform = "4.3.4" velocity = "3.1.1" guice = "7.0.0" configurate = "4.2.0-YAML-COMMENTED-SNAPSHOT" scoreboard = "2.0.1" shadowJar = "8.3.5" pluginYml = "0.6.0" -guava = "33.0.0-jre" +guava = "33.3.1-jre" apacheCommons = "3.14.0" ebean = "15.1.0" flyway = "10.12.0" -protoc = "4.27.1" +protoc = "4.29.1" super-vanish = "6.2.18-3" inventory = "1.0.0-ee06bf8" mock-bukkit = "3.9.0" @@ -32,6 +31,8 @@ brigadier = "1.0.18" commodore = "2.2" google-auto-value = "1.11.0" google-auto-service = "1.1.1" +luckperms = "5.4" +neznamy-tab-api = "5.0.3" [libraries] hikariCP = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikariCP" } @@ -88,6 +89,8 @@ nookure-core-inventory = { group = "com.nookure.core", name = "NookCore-Inventor mockBukkit = { group = "com.github.seeseemelk", name = "MockBukkit-v1.20", version.ref = "mock-bukkit" } mojang-brigadier = { group = "com.mojang", name = "brigadier", version.ref = "brigadier" } lucko-commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" } +lucko-luckperms = { group = "net.luckperms", name = "api", version.ref = "luckperms" } +neznamy-tab-api = { group = "com.github.NEZNAMY", name = "TAB-API", version.ref = "neznamy-tab-api" } [bundles] invAPI = ["invCore", "invAdvancedSlots", "invPagination", "configurableGui"] @@ -103,4 +106,5 @@ grgitPublish = { id = "org.ajoberstar.git-publish", version = "4.2.1" } ebean = { id = "io.ebean", version.ref = "ebean" } libby = { id = "xyz.kyngs.libby.plugin", version = "1.0.0" } runPaper = { id = "xyz.jpenilla.run-paper", version = "2.3.0" } -testLogger = { id = "com.adarshr.test-logger", version = "4.0.0"} \ No newline at end of file +testLogger = { id = "com.adarshr.test-logger", version = "4.0.0"} +minotaur = { id = "com.modrinth.minotaur", version = "2.+" } \ No newline at end of file