commit 33a558d56ebf9b9e0597e5f9b889eb0c31cec897 Author: EpicKnarvik97 Date: Wed Dec 17 14:42:01 2025 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87d4436 --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c4f7ca3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + net.knarcraft + LockedPortals + 1.0-SNAPSHOT + jar + + LockedPortals + + + 16 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0 + + + package + + shade + + + false + + + org.jetbrains.annotations + net.knarcraft.blacksmith.lib.annotations + + + + + org.jetbrains:annotations + + org/jetbrains/annotations/** + + + + + + + + + + + src/main/resources + true + + + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.21.10-R0.1-SNAPSHOT + provided + + + diff --git a/src/main/java/net/knarcraft/lockedportals/LockedPortals.java b/src/main/java/net/knarcraft/lockedportals/LockedPortals.java new file mode 100644 index 0000000..718f530 --- /dev/null +++ b/src/main/java/net/knarcraft/lockedportals/LockedPortals.java @@ -0,0 +1,125 @@ +package net.knarcraft.lockedportals; + +import net.knarcraft.lockedportals.command.ToggleNetherCommand; +import net.knarcraft.lockedportals.command.ToggleTheEndCommand; +import net.knarcraft.lockedportals.listener.LockedPortalsListener; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.PluginCommand; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.logging.Level; + +public final class LockedPortals extends JavaPlugin { + + private static LockedPortals instance; + private boolean netherEnabled = false; + private boolean theEndEnabled = false; + + @Override + public void onEnable() { + // Plugin startup logic + instance = this; + getServer().getPluginManager().registerEvents(new LockedPortalsListener(), this); + PluginCommand toggleNetherExecutor = this.getCommand("toggleNether"); + if (toggleNetherExecutor != null) { + toggleNetherExecutor.setExecutor(new ToggleNetherCommand()); + } + PluginCommand toggleTheEndExecutor = this.getCommand("toggleTheEnd"); + if (toggleTheEndExecutor != null) { + toggleTheEndExecutor.setExecutor(new ToggleTheEndCommand()); + } + + Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> { + Instant instant = Instant.now(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + int hour = localDateTime.getHour(); + int remainder = hour % 3; + if (remainder == 0) { + // Nether open + if (!isNetherEnabled()) { + instance.setNetherEnabled(true); + } + if (isTheEndEnabled()) { + instance.teleportPlayers(World.Environment.THE_END); + instance.setTheEndEnabled(false); + } + } else if (remainder == 1) { + // The end open + if (isNetherEnabled()) { + instance.teleportPlayers(World.Environment.NETHER); + instance.setNetherEnabled(false); + } + if (!isTheEndEnabled()) { + instance.setTheEndEnabled(true); + } + } else { + // Both closed + if (isNetherEnabled()) { + instance.teleportPlayers(World.Environment.NETHER); + instance.setNetherEnabled(false); + } + if (isTheEndEnabled()) { + instance.teleportPlayers(World.Environment.THE_END); + instance.setTheEndEnabled(false); + } + } + }, 100, 1200); + } + + @Override + public void onDisable() { + // Plugin shutdown logic + } + + public void setNetherEnabled(boolean enabled) { + netherEnabled = enabled; + } + + public void setTheEndEnabled(boolean enabled) { + theEndEnabled = enabled; + } + + public boolean isNetherEnabled() { + return netherEnabled; + } + + public boolean isTheEndEnabled() { + return theEndEnabled; + } + + public void teleportPlayers(World.Environment environment) { + World mainWorld = Bukkit.getWorld("world"); + if (mainWorld == null) { + getLogger().log(Level.SEVERE, "Unable to find the specified main world"); + return; + } + for (World world : Bukkit.getWorlds()) { + if (!world.getEnvironment().equals(environment)) { + continue; + } + for (Player player : world.getPlayers()) { + // Teleport the player to the world's spawn + player.teleport(mainWorld.getSpawnLocation()); + // Make the player go to their normal spawn + if (!player.performCommand("spawn")) { + getLogger().log(Level.SEVERE, "Unable to send player to their actual spawn"); + } + } + } + } + + /** + * Gets an instance of this plugin + * + * @return

An instance of this plugin

+ */ + public static LockedPortals getInstance() { + return LockedPortals.instance; + } + +} diff --git a/src/main/java/net/knarcraft/lockedportals/command/ToggleNetherCommand.java b/src/main/java/net/knarcraft/lockedportals/command/ToggleNetherCommand.java new file mode 100644 index 0000000..69f1cc8 --- /dev/null +++ b/src/main/java/net/knarcraft/lockedportals/command/ToggleNetherCommand.java @@ -0,0 +1,34 @@ +package net.knarcraft.lockedportals.command; + +import net.knarcraft.lockedportals.LockedPortals; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ToggleNetherCommand implements TabExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] strings) { + LockedPortals instance = LockedPortals.getInstance(); + boolean enabled = instance.isNetherEnabled(); + if (enabled) { + instance.teleportPlayers(World.Environment.NETHER); + } + instance.setNetherEnabled(!enabled); + commandSender.sendMessage("Nether access " + (enabled ? "disabled" : "enabled")); + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, + @NotNull String s, @NotNull String[] strings) { + return List.of(); + } + +} diff --git a/src/main/java/net/knarcraft/lockedportals/command/ToggleTheEndCommand.java b/src/main/java/net/knarcraft/lockedportals/command/ToggleTheEndCommand.java new file mode 100644 index 0000000..e05051b --- /dev/null +++ b/src/main/java/net/knarcraft/lockedportals/command/ToggleTheEndCommand.java @@ -0,0 +1,34 @@ +package net.knarcraft.lockedportals.command; + +import net.knarcraft.lockedportals.LockedPortals; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ToggleTheEndCommand implements TabExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] strings) { + LockedPortals instance = LockedPortals.getInstance(); + boolean enabled = instance.isTheEndEnabled(); + if (enabled) { + instance.teleportPlayers(World.Environment.THE_END); + } + instance.setTheEndEnabled(!enabled); + commandSender.sendMessage("The End access " + (enabled ? "disabled" : "enabled")); + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, + @NotNull String s, @NotNull String[] strings) { + return List.of(); + } + +} diff --git a/src/main/java/net/knarcraft/lockedportals/listener/LockedPortalsListener.java b/src/main/java/net/knarcraft/lockedportals/listener/LockedPortalsListener.java new file mode 100644 index 0000000..f653ff8 --- /dev/null +++ b/src/main/java/net/knarcraft/lockedportals/listener/LockedPortalsListener.java @@ -0,0 +1,139 @@ +package net.knarcraft.lockedportals.listener; + +import net.knarcraft.lockedportals.LockedPortals; +import net.knarcraft.lockedportals.utility.MessageHelper; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.PortalType; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPortalEnterEvent; +import org.bukkit.event.entity.EntityTeleportEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.world.PortalCreateEvent; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LockedPortalsListener implements Listener { + + private final Map cancelledTeleportations = new HashMap<>(); + + // TODO: Cancel creation of Nether and End portals + // TODO: Cancel entering a netherite portal or end portal, unless an event is in progress + // TODO: Teleport any joining portal away from the Nether or the End if and event is not in progress + // TODO: On event end, tell and teleport all online players + + // Title and Subtitle: player.sendTitle + // Action bar: player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); or paper: player.sendActionBar("Test"); + + @EventHandler + public void onJoin(@NotNull PlayerJoinEvent event) { + LockedPortals instance = LockedPortals.getInstance(); + World.Environment environment = event.getPlayer().getLocation().getWorld().getEnvironment(); + if ((environment == World.Environment.NETHER && !instance.isNetherEnabled()) || + (environment == World.Environment.THE_END && !instance.isTheEndEnabled())) { + sendPlayerBack(event.getPlayer(), instance.getLogger()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onNetherOrEndPlayerTeleport(@NotNull PlayerTeleportEvent event) { + World world = event.getTo().getWorld(); + if ((world.getEnvironment().equals(World.Environment.NETHER) && !LockedPortals.getInstance().isNetherEnabled()) || + (world.getEnvironment().equals(World.Environment.THE_END) && !LockedPortals.getInstance().isTheEndEnabled())) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onNetherOrEndTeleport(@NotNull EntityTeleportEvent event) { + Location destination = event.getTo(); + if (destination == null) { + return; + } + World world = destination.getWorld(); + if ((world.getEnvironment().equals(World.Environment.NETHER) && !LockedPortals.getInstance().isNetherEnabled()) || + (world.getEnvironment().equals(World.Environment.THE_END) && !LockedPortals.getInstance().isTheEndEnabled())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onNetherPortalCreate(@NotNull PortalCreateEvent event) { + if (event.getReason() == PortalCreateEvent.CreateReason.FIRE) { + event.setCancelled(true); + } + } + + @EventHandler + public void onStrongholdActivate(@NotNull PlayerInteractEvent event) { + if (event.getClickedBlock() != null && event.getClickedBlock().getType() == Material.END_PORTAL_FRAME) { + event.setCancelled(true); + } + } + + @EventHandler + public void onNetherPortalEnter(@NotNull EntityPortalEnterEvent event) { + LockedPortals instance = LockedPortals.getInstance(); + PortalType portalType = event.getPortalType(); + if ((instance.isNetherEnabled() && portalType == PortalType.NETHER) || + (instance.isTheEndEnabled() && portalType == PortalType.ENDER) || portalType == PortalType.CUSTOM || + portalType == PortalType.END_GATEWAY || !(event.getEntity() instanceof Player player)) { + return; + } + + event.setCancelled(true); + player.resetTitle(); + if (portalType == PortalType.NETHER) { + MessageHelper.sendTitles(player, "Nether is closed", "Come back when the time is right"); + teleportBack(player); + } + if (portalType == PortalType.ENDER) { + MessageHelper.sendTitles(player, "End is closed", "Come back when the time is right"); + } + MessageHelper.sendActionBar(player, "Check the website for event details and schedules"); + } + + /** + * Teleports a player back to the direction they came from + * + * @param player

The player to teleport

+ */ + private void teleportBack(@NotNull Player player) { + Long cancelled = cancelledTeleportations.get(player); + if (cancelled != null && System.currentTimeMillis() - cancelled < 100) { + cancelledTeleportations.put(player, System.currentTimeMillis()); + return; + } + cancelledTeleportations.put(player, System.currentTimeMillis()); + player.teleport(player.getLocation().clone().add(player.getLocation().getDirection().multiply( + new Vector(1, 0, 1)).multiply(-1).normalize())); + } + + private void sendPlayerBack(@NotNull Player player, @NotNull Logger logger) { + World mainWorld = Bukkit.getWorld("world"); + if (mainWorld == null) { + logger.log(Level.SEVERE, "Unable to find the specified main world"); + return; + } + + // Teleport the player to the world's spawn + player.teleport(mainWorld.getSpawnLocation()); + // Make the player go to their normal spawn + if (!player.performCommand("spawn")) { + logger.log(Level.SEVERE, "Unable to send player to their actual spawn"); + } + } + +} diff --git a/src/main/java/net/knarcraft/lockedportals/utility/MessageHelper.java b/src/main/java/net/knarcraft/lockedportals/utility/MessageHelper.java new file mode 100644 index 0000000..83cbf48 --- /dev/null +++ b/src/main/java/net/knarcraft/lockedportals/utility/MessageHelper.java @@ -0,0 +1,42 @@ +package net.knarcraft.lockedportals.utility; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.title.Title; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; + +/** + * A helper class for dealing with messages and titles + */ +public final class MessageHelper { + + /** + * Sends the set title and subtitle to the given player + * + * @param player

The player to send the title and subtitle to

+ * @param title

The title to send

+ * @param subtitle

The subtitle to send

+ */ + public static void sendTitles(@NotNull Player player, @NotNull String title, @NotNull String subtitle) { + Title.Times times = Title.Times.times(Duration.ofMillis(200), Duration.ofMillis(1700), Duration.ofMillis(400)); + TextComponent titleComponent = Component.text().color(TextColor.fromCSSHexString("#CC0000")).content(title).build(); + TextComponent subtitleComponent = Component.text().color(TextColor.fromCSSHexString("#674EA7")).content(subtitle).build(); + player.showTitle(Title.title(titleComponent, subtitleComponent, times)); + } + + /** + * Sends an action bar message to a player + * + * @param player

The player to send the action bar message to

+ * @param message

The message to send

+ */ + public static void sendActionBar(@NotNull Player player, @NotNull String message) { + TextComponent component = Component.text().color(TextColor.fromCSSHexString("#8FF746")).content(message).build(); + player.sendActionBar(component); + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..01b1d9f --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,14 @@ +name: LockedPortals +version: '${project.version}' +main: net.knarcraft.lockedportals.LockedPortals +api-version: '1.21' + +commands: + toggleNether: + description: "Toggles Nether availability" + usage: "" + permission: splitfactions.admin + toggleTheEnd: + description: "Toggles The End availability" + usage: "" + permission: splitfactions.admin \ No newline at end of file