Initial commit

This commit is contained in:
2025-12-17 14:42:01 +01:00
commit 33a558d56e
8 changed files with 585 additions and 0 deletions

113
.gitignore vendored Normal file
View File

@@ -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/

84
pom.xml Normal file
View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.knarcraft</groupId>
<artifactId>LockedPortals</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>LockedPortals</name>
<properties>
<java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.jetbrains.annotations</pattern>
<shadedPattern>net.knarcraft.blacksmith.lib.annotations</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>org.jetbrains:annotations</artifact>
<includes>
<include>org/jetbrains/annotations/**</include>
</includes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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 <p>An instance of this plugin</p>
*/
public static LockedPortals getInstance() {
return LockedPortals.instance;
}
}

View File

@@ -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<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s, @NotNull String[] strings) {
return List.of();
}
}

View File

@@ -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<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s, @NotNull String[] strings) {
return List.of();
}
}

View File

@@ -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<Player, Long> 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 <p>The player to teleport</p>
*/
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");
}
}
}

View File

@@ -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 <p>The player to send the title and subtitle to</p>
* @param title <p>The title to send</p>
* @param subtitle <p>The subtitle to send</p>
*/
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 <p>The player to send the action bar message to</p>
* @param message <p>The message to send</p>
*/
public static void sendActionBar(@NotNull Player player, @NotNull String message) {
TextComponent component = Component.text().color(TextColor.fromCSSHexString("#8FF746")).content(message).build();
player.sendActionBar(component);
}
}

View File

@@ -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: "<command>"
permission: splitfactions.admin
toggleTheEnd:
description: "Toggles The End availability"
usage: "<command>"
permission: splitfactions.admin