Add server modes, more event listeners and more

This commit is contained in:
JeremyStar™ 2024-11-21 15:14:57 +01:00
parent 44dab9e11b
commit 44fd5ed427
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
43 changed files with 1699 additions and 68 deletions

View file

@ -27,6 +27,9 @@ INSTANCE,Health ,@NotNull ,PSPlayer ,heal
INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints" INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints"
INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints, @NotNull Entity entity" INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints, @NotNull Entity entity"
INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints, @NotNull DamageSource damageSource" INSTANCE,Health ,@NotNull ,PSPlayer ,damage ,"double healthPoints, @NotNull DamageSource damageSource"
INSTANCE,Health ,@NotNull ,PSPlayer ,kill ,""
INSTANCE,Health ,@NotNull ,PSPlayer ,kill ,"@NotNull Entity entity"
INSTANCE,Health ,@NotNull ,PSPlayer ,kill ,"@NotNull DamageSource damageSource"
INSTANCE,Hunger , ,int ,getHunger ,"" INSTANCE,Hunger , ,int ,getHunger ,""
INSTANCE,Hunger , ,float ,getSaturation ,"" INSTANCE,Hunger , ,float ,getSaturation ,""
INSTANCE,Hunger ,@NotNull ,PSPlayer ,setHunger ,"int hungerPoints" INSTANCE,Hunger ,@NotNull ,PSPlayer ,setHunger ,"int hungerPoints"

1 TYPE CATEGORY NULLVALUE RETURN TYPE METHODNAME METHODARGUMENTS
27 INSTANCE Health @NotNull PSPlayer damage double healthPoints
28 INSTANCE Health @NotNull PSPlayer damage double healthPoints, @NotNull Entity entity
29 INSTANCE Health @NotNull PSPlayer damage double healthPoints, @NotNull DamageSource damageSource
30 INSTANCE Health @NotNull PSPlayer kill
31 INSTANCE Health @NotNull PSPlayer kill @NotNull Entity entity
32 INSTANCE Health @NotNull PSPlayer kill @NotNull DamageSource damageSource
33 INSTANCE Hunger int getHunger
34 INSTANCE Hunger float getSaturation
35 INSTANCE Hunger @NotNull PSPlayer setHunger int hungerPoints

View file

@ -24,6 +24,8 @@ import net.luckperms.api.util.Tristate;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.time.Clock;
import java.time.ZoneId;
import java.util.List; import java.util.List;
/** /**
@ -86,6 +88,22 @@ public final class BuildOptions {
// -----> Settings // -----> Settings
/**
* {@link Clock} instance used for calculating time.
*
* @see Clock
* @since v1-release0
*/
public static final @NotNull Clock SETTINGS_CLOCK = Clock.systemDefaultZone();
/**
* The rate at which the tab list shall be updated.
*
* @see TabListHandler
* @since v1-release0
*/
public static final long SETTINGS_TABLIST_UPDATERATE = 20L;
/** /**
* An array containing all bad effects. * An array containing all bad effects.
* *
@ -121,14 +139,6 @@ public final class BuildOptions {
PotionEffectType.POISON PotionEffectType.POISON
); );
/**
* The rate at which the tab list shall be updated.
*
* @see TabListHandler
* @since v1-release0
*/
public static final long SETTINGS_TABLIST_UPDATERATE = 20L;
// -----> Fixes and unfixes // -----> Fixes and unfixes
/** /**

View file

@ -25,13 +25,21 @@ import de.jeremystartm.pickshadow.extension.command.general.AnnounceCommand;
import de.jeremystartm.pickshadow.extension.command.general.replacement.GamemodeCommand; import de.jeremystartm.pickshadow.extension.command.general.replacement.GamemodeCommand;
import de.jeremystartm.pickshadow.extension.command.general.replacement.HelpCommand; import de.jeremystartm.pickshadow.extension.command.general.replacement.HelpCommand;
import de.jeremystartm.pickshadow.extension.command.general.replacement.MessageCommand; import de.jeremystartm.pickshadow.extension.command.general.replacement.MessageCommand;
import de.jeremystartm.pickshadow.extension.command.multi.SpawnCommand;
import de.jeremystartm.pickshadow.extension.command.survivalcheat.FeedCommand; import de.jeremystartm.pickshadow.extension.command.survivalcheat.FeedCommand;
import de.jeremystartm.pickshadow.extension.command.survivalcheat.HealCommand; import de.jeremystartm.pickshadow.extension.command.survivalcheat.HealCommand;
import de.jeremystartm.pickshadow.extension.command.survivalcheat.ToggleDownfallCommand; import de.jeremystartm.pickshadow.extension.command.survivalcheat.ToggleDownfallCommand;
import de.jeremystartm.pickshadow.extension.command.general.*; import de.jeremystartm.pickshadow.extension.command.general.*;
import de.jeremystartm.pickshadow.extension.command.survival.HomeCommand; import de.jeremystartm.pickshadow.extension.command.survival.HomeCommand;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import de.jeremystartm.pickshadow.extension.implementation.mode.GeneralMode;
import de.jeremystartm.pickshadow.extension.implementation.mode.HubMode;
import de.jeremystartm.pickshadow.extension.implementation.mode.SurvivalCheatMode;
import de.jeremystartm.pickshadow.extension.implementation.mode.SurvivalMode;
import de.jeremystartm.pickshadow.extension.listener.ChatListener; import de.jeremystartm.pickshadow.extension.listener.ChatListener;
import de.jeremystartm.pickshadow.extension.listener.ConnectionListener; import de.jeremystartm.pickshadow.extension.listener.ConnectionListener;
import de.jeremystartm.pickshadow.extension.listener.PlayerEventListener;
import de.jeremystartm.pickshadow.extension.listener.WorldEventListener;
import de.jeremystartm.pickshadow.extension.misc.BukkitLoggingAdapter; import de.jeremystartm.pickshadow.extension.misc.BukkitLoggingAdapter;
import de.jeremystartm.pickshadow.extension.misc.Scheduler; import de.jeremystartm.pickshadow.extension.misc.Scheduler;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
@ -110,6 +118,11 @@ public final class Extension extends JavaPlugin {
Logger.info("Initializing"); Logger.info("Initializing");
try { try {
Logger.info("Initialized in " + Miscellaneous.measureExecutionTime(() -> { Logger.info("Initialized in " + Miscellaneous.measureExecutionTime(() -> {
// Reload configuration to apply world-related settings
Logger.verb("Reloading configuration to apply world-related settings");
ExtensionConfiguration.getInstance().loadDefaultConfiguration();
ExtensionConfiguration.getInstance().loadConfiguration();
Logger.verb("Checking environment"); Logger.verb("Checking environment");
// Check for PaperMC // Check for PaperMC
if (!isPaper()) if (!isPaper())
@ -136,6 +149,9 @@ public final class Extension extends JavaPlugin {
new TrollCommand(); new TrollCommand();
new SpawnCommand();
new HomeCommand(); new HomeCommand();
@ -143,9 +159,30 @@ public final class Extension extends JavaPlugin {
new HealCommand(); new HealCommand();
new ToggleDownfallCommand(); new ToggleDownfallCommand();
Logger.verb("Initializing modes");
{
ServerMode[] modes = new ServerMode[]{
new GeneralMode(),
new HubMode(),
new SurvivalCheatMode(),
new SurvivalMode(),
};
for (ServerMode mode : modes)
if (ExtensionConfiguration.getInstance().getEnabledModes().contains(mode.getName())) {
try {
mode.initialize();
} catch (Exception exception) {
Logger.crash("Failed initializing mode " + mode.getName(), exception);
}
}
}
Logger.verb("Registering listeners"); Logger.verb("Registering listeners");
Bukkit.getPluginManager().registerEvents(new ConnectionListener(), this); new ChatListener();
Bukkit.getPluginManager().registerEvents(new ChatListener(), this); new ConnectionListener();
new PlayerEventListener();
new WorldEventListener();
Logger.verb("Starting schedulers"); Logger.verb("Starting schedulers");
Bukkit.getServer().getGlobalRegionScheduler().runAtFixedRate(Extension.getInstance(), Scheduler::server, 1L, 1L); Bukkit.getServer().getGlobalRegionScheduler().runAtFixedRate(Extension.getInstance(), Scheduler::server, 1L, 1L);
@ -163,6 +200,16 @@ public final class Extension extends JavaPlugin {
@Override @Override
public void onDisable() { public void onDisable() {
Logger.info("Shutting down"); Logger.info("Shutting down");
// Unwind all server modes
for (ServerMode mode : ServerMode.getInitializedModes(false)) {
try {
mode.unwind();
} catch (Exception exception) {
Logger.crash("Failed unwinding mode " + mode.getName(), exception);
}
}
Logger.info("Shut down in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms"); Logger.info("Shut down in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms");
} }

View file

@ -21,11 +21,18 @@ package de.jeremystartm.pickshadow.extension;
import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion;
import de.staropensource.engine.base.implementable.Configuration; import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.PropertiesReader; import de.staropensource.engine.base.utility.PropertiesReader;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
@Getter @Getter
@SuppressWarnings({ "JavadocDeclaration" }) @SuppressWarnings({ "JavadocDeclaration" })
public final class ExtensionConfiguration extends Configuration { public final class ExtensionConfiguration extends Configuration {
@ -110,7 +117,21 @@ public final class ExtensionConfiguration extends Configuration {
* @return enabled extension modes * @return enabled extension modes
* @since v1-release0 * @since v1-release0
*/ */
private String[] enabledModes; private List<@NotNull String> enabledModes;
/**
* Contains the location of
* the server's spawn point.
*
* @since v1-release0
* -- GETTER --
* Returns the location of
* the server's spawn point.
*
* @return spawn location
* @since v1-release0
*/
private Location spawnLocation;
/** /**
* Creates and initializes an instance of this class. * Creates and initializes an instance of this class.
@ -133,7 +154,50 @@ public final class ExtensionConfiguration extends Configuration {
case "debug" -> debug = parser.getBoolean(group + property); case "debug" -> debug = parser.getBoolean(group + property);
case "debugStaticCompletion" -> debugStaticCompletion = parser.getBoolean(group + property); case "debugStaticCompletion" -> debugStaticCompletion = parser.getBoolean(group + property);
case "enabledModes" -> enabledModes = parser.getString(group + property).split(","); case "enabledModes" -> enabledModes = Arrays.stream(parser.getString(group + property).split(",")).toList();
case "spawnLocation" -> {
if (Bukkit.getWorlds().isEmpty())
return;
String[] input = parser.getString(group + property).split(",");
World world = null;
double x;
double y;
double z;
float yaw;
float pitch;
// Check length of 'input'
if (input.length < 5 || input.length > 6) {
Logger.error("The spawn location '" + parser.getString(group + property) + "' is invalid (must contain at 5-6 elements)");
return;
}
// Parse doubles
try {
x = Double.parseDouble(input[0]);
y = Double.parseDouble(input[1]);
z = Double.parseDouble(input[2]);
yaw = Float.parseFloat(input[3]);
pitch = Float.parseFloat(input[4]);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException exception) {
Logger.error("The spawn location '" + parser.getString(group + property) + "' is invalid (format: <x>,<y>,<z>,<yaw>,<pitch>[,<world>])");
return;
}
// Get world
if (input.length == 6) {
world = Bukkit.getWorld(input[5]);
if (world == null) {
Logger.error("The spawn location '" + parser.getString(group + property) + "' is invalid (world \"" + input[5] + "\" not found)");
return;
}
}
Logger.diag("Saving location X" + x + " Y" + y + " Z" + z + " YAW" + yaw + " PITCH" + pitch + " WORLD" + (world == null ? "<none>" : world.getName()));
spawnLocation = new Location(world, x, y, z, yaw, pitch);
}
} }
} catch (NullPointerException ignored) {} } catch (NullPointerException ignored) {}
} }
@ -153,7 +217,17 @@ public final class ExtensionConfiguration extends Configuration {
debug = false; debug = false;
debugStaticCompletion = false; debugStaticCompletion = false;
enabledModes = new String[]{ "general" }; enabledModes = List.of( "general" );
// World-related settings
// Will only execute after all worlds have been loaded
if (Bukkit.getWorlds().isEmpty())
spawnLocation = null;
else {
spawnLocation = new Location(Bukkit.getWorld("world"), 0, 0, 0, 0 ,0);
if (spawnLocation.getWorld() == null)
Logger.crash("Default world \"world\" could not be found");
}
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -164,6 +238,7 @@ public final class ExtensionConfiguration extends Configuration {
case "debugStaticCompletion" -> debugStaticCompletion; case "debugStaticCompletion" -> debugStaticCompletion;
case "enabledModes" -> enabledModes; case "enabledModes" -> enabledModes;
case "spawnLocation" -> spawnLocation;
default -> null; default -> null;
}; };
} }

View file

@ -33,7 +33,6 @@ import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.util.*; import java.util.*;
@ -90,9 +89,17 @@ public abstract class Command implements CommandExecutor {
* @since v1-release0 * @since v1-release0
*/ */
public Command(@NotNull Command.Information information, @NotNull String... commands) throws IllegalArgumentException { public Command(@NotNull Command.Information information, @NotNull String... commands) throws IllegalArgumentException {
boolean disallowedByMode = !Arrays.stream(ExtensionConfiguration.getInstance().getEnabledModes()).toList().contains(information.mode()); boolean disallowedByMode = true;
this.information = information; this.information = information;
// Check if command is allowed
for (String modeEnabled : ExtensionConfiguration.getInstance().getEnabledModes())
for (String modeCommand : information.modes())
if (modeEnabled.equals(modeCommand)) {
disallowedByMode = false;
break;
}
for (String command : commands) { for (String command : commands) {
PluginCommand pluginCommand = Bukkit.getPluginCommand(command); PluginCommand pluginCommand = Bukkit.getPluginCommand(command);
if (pluginCommand == null) if (pluginCommand == null)
@ -265,8 +272,12 @@ public abstract class Command implements CommandExecutor {
if (!sender.hasPermission(permission)) { if (!sender.hasPermission(permission)) {
sender.sendRichMessage( sender.sendRichMessage(
TranslationManager.get(LanguageString.ERROR_MISSING_PERM, sender, true) TranslationManager.get(
.replace("%permission%", permission) LanguageString.ERROR_MISSING_PERM,
sender,
true,
entry("permission", permission)
)
); );
return true; return true;
} }
@ -297,6 +308,58 @@ public abstract class Command implements CommandExecutor {
return false; return false;
} }
/**
* Performs a permission check, sends the sender
* an error message and then returns.
* Useful for easy permission checks.
*
* @param sender sender to check
* @param permission permission to check for
* @return {@code true} if the permission is missing, {@code false} otherwise
* @since v1-release0
*/
protected static boolean checkPermission(@NotNull CommandSender sender, @NotNull String permission, @NotNull String permissionReported) {
if (sender instanceof ServerCommandSender)
return false;
if (!sender.hasPermission(permission)) {
sender.sendRichMessage(
TranslationManager.get(
LanguageString.ERROR_MISSING_PERM,
sender,
true,
entry("permission", permissionReported)
)
);
return true;
}
return false;
}
/**
* Performs a permission check, sends the sender
* an error message and then returns.
* Useful for easy permission checks.
*
* @param player {@link PSPlayer} to check
* @param permission permission to check for
* @return {@code true} if the permission is missing, {@code false} otherwise
* @since v1-release0
*/
protected static boolean checkPermission(@NotNull PSPlayer player, @NotNull String permission, @NotNull String permissionReported) {
if (!player.hasPermission(permission)) {
player.messageTranslatable(
LanguageString.ERROR_MISSING_PERM,
true,
entry("permission", permissionReported)
);
return true;
}
return false;
}
/** /**
* Determines the target player. * Determines the target player.
* *
@ -425,7 +488,7 @@ public abstract class Command implements CommandExecutor {
* @param aliases Array of acceptable aliases * @param aliases Array of acceptable aliases
* @param description Description of the command * @param description Description of the command
* @param syntax Syntax of the command * @param syntax Syntax of the command
* @param mode Plugin mode of the command * @param modes Server modes the command shall be enabled in
* @param executionOrder Execution order in which the three execution methods shall be invoked. * @param executionOrder Execution order in which the three execution methods shall be invoked.
* <p> * <p>
* Must contain exactly three elements, representing this order: * Must contain exactly three elements, representing this order:
@ -453,9 +516,9 @@ public abstract class Command implements CommandExecutor {
@NotNull String @NotNull [] aliases, @NotNull String @NotNull [] aliases,
@NotNull String description, @NotNull String description,
@NotNull String syntax, @NotNull String syntax,
@NotNull String mode, @NotNull String @org.jetbrains.annotations.NotNull [] modes,
@NotNull String @Nullable [] executionOrder, @NotNull String @Nullable [] executionOrder,
@NotNull Command.ExecutionTarget executionTarget @NotNull ExecutionTarget executionTarget
) { ) {
/** /**
* Creates and initializes an * Creates and initializes an

View file

@ -28,7 +28,6 @@ import de.jeremystartm.pickshadow.extension.api.type.PlayerAttribute;
import de.jeremystartm.pickshadow.extension.api.type.PlayerDamageState; import de.jeremystartm.pickshadow.extension.api.type.PlayerDamageState;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import fr.mrmicky.fastboard.adventure.FastBoard; import fr.mrmicky.fastboard.adventure.FastBoard;
import io.netty.util.NettyRuntime;
import io.papermc.paper.entity.TeleportFlag; import io.papermc.paper.entity.TeleportFlag;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@ -38,7 +37,6 @@ import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.title.Title; import net.kyori.adventure.title.Title;
import net.minecraft.network.DisconnectionDetails;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
@ -487,7 +485,7 @@ public final class PSPlayer {
* Damages this player. * Damages this player.
* *
* @param healthPoints amount of damage to deal in health points * @param healthPoints amount of damage to deal in health points
* @param damageSource information about what the damage caused * @param damageSource information about who or what caused the damage
* @return this instance * @return this instance
* @since v1-release0 * @since v1-release0
*/ */
@ -497,6 +495,42 @@ public final class PSPlayer {
return this; return this;
} }
/**
* Kills this player.
*
* @return this instance
* @since v1-release0
*/
public @NotNull PSPlayer kill() {
bukkitPlayer.damage(bukkitPlayer.getHealth());
return this;
}
/**
* Kills this player.
*
* @param entity entity which killed this player
* @return this instance
* @since v1-release0
*/
public @NotNull PSPlayer kill(@NotNull Entity entity) {
bukkitPlayer.damage(bukkitPlayer.getHealth(), entity);
return this;
}
/**
* Kills this player.
*
* @param damageSource information about who or what caused the damage
* @return this instance
* @since v1-release0
*/
@SuppressWarnings("UnstableApiUsage")
public @NotNull PSPlayer kill(@NotNull DamageSource damageSource) {
bukkitPlayer.damage(bukkitPlayer.getHealth(), damageSource);
return this;
}
// -----> Hunger // -----> Hunger
/** /**

View file

@ -61,10 +61,15 @@ public enum LanguageString {
CONNECTION_ERROR_SCHEDULER, CONNECTION_ERROR_SCHEDULER,
// Command /psse // Command /psse
EXTENSIONCMD_GREETER, EXTENSIONCMD,
EXTENSIONCMD_KILLJVM,
EXTENSIONCMD_LICENSE, EXTENSIONCMD_LICENSE,
EXTENSIONCMD_SOURCE, EXTENSIONCMD_SOURCE,
EXTENSIONCMD_MODES,
EXTENSIONCMD_MODES_ENABLE,
EXTENSIONCMD_MODES_DISABLE,
EXTENSIONCMD_KILLJVM,
EXTENSIONCMD_INTERNAL_DEVELOPMENTMODE,
EXTENSIONCMD_INTERNAL_SENDTOSERVER,
// Command /pssp // Command /pssp
LEGACYEXTENSIONCMD, LEGACYEXTENSIONCMD,
@ -147,6 +152,10 @@ public enum LanguageString {
SPEED_FLY, SPEED_FLY,
SPEED_WALK, SPEED_WALK,
// Command /spawn
SPAWN,
SPAWN_REMOTE,
// Event for chat commands // Event for chat commands
CHATCOMMAND_ERROR_NAMESPACE, CHATCOMMAND_ERROR_NAMESPACE,
} }

View file

@ -67,7 +67,7 @@ public final class TemplateCommand extends CommandForced {
new String[]{ "alias 1", "alias 2" }, new String[]{ "alias 1", "alias 2" },
"An example command", "An example command",
"/cmd [--verbose] <arg1|arg2|arg3>", "/cmd [--verbose] <arg1|arg2|arg3>",
"mode", new String[] { "mode" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "cmd"); ), "cmd");

View file

@ -65,7 +65,7 @@ public final class AnnounceCommand extends Command {
new String[]{ "announce", "announcement", "broadcast", "bc" }, new String[]{ "announce", "announcement", "broadcast", "bc" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "announce"); ), "announce");

View file

@ -72,7 +72,7 @@ public final class ClearChatCommand extends Command {
new String[]{ "clearchat", "chatclear", "cc" }, new String[]{ "clearchat", "chatclear", "cc" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "clearchat"); ), "clearchat");

View file

@ -19,21 +19,27 @@
package de.jeremystartm.pickshadow.extension.command.general; package de.jeremystartm.pickshadow.extension.command.general;
import com.google.gson.Gson;
import de.jeremystartm.pickshadow.common.PSSKInformation; import de.jeremystartm.pickshadow.common.PSSKInformation;
import de.jeremystartm.pickshadow.extension.BuildOptions;
import de.jeremystartm.pickshadow.extension.api.command.Command; import de.jeremystartm.pickshadow.extension.api.command.Command;
import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion;
import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; import de.jeremystartm.pickshadow.extension.api.command.TabCompletion;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory; import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory;
import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString;
import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager;
import de.jeremystartm.pickshadow.extension.api.type.PlayerAttribute; import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import de.staropensource.engine.base.utility.PlaceholderEngine; import de.jeremystartm.pickshadow.extension.misc.Miscellaneous;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.ListFormatter;
import lombok.Getter; import lombok.Getter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.stream.Collectors;
import static java.util.Map.entry; import static java.util.Map.entry;
/** /**
@ -57,6 +63,8 @@ public final class ExtensionCommand extends Command {
private final @NotNull TabCompletion completion = new StaticTabCompletion() private final @NotNull TabCompletion completion = new StaticTabCompletion()
.add("", 0, "license", "pickshadow.command.extension") .add("", 0, "license", "pickshadow.command.extension")
.add("", 0, "source", "pickshadow.command.extension") .add("", 0, "source", "pickshadow.command.extension")
.add("", 0, "modes", "pickshadow.command.extension")
.add("", 0, "disableModes", "pickshadow.command.extension.advanced")
.add("", 0, "killjvm", "pickshadow.command.extension.advanced") .add("", 0, "killjvm", "pickshadow.command.extension.advanced")
.add("", 0, "debug", "pickshadow.command.extension.advanced"); .add("", 0, "debug", "pickshadow.command.extension.advanced");
@ -72,7 +80,7 @@ public final class ExtensionCommand extends Command {
new String[]{ "psse", "pickshadow", "server", "about", "version", "ver" }, new String[]{ "psse", "pickshadow", "server", "about", "version", "ver" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "psse"); ), "psse");
@ -81,10 +89,10 @@ public final class ExtensionCommand extends Command {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
protected void invokeAll(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] arguments) throws Exception { protected void invokeAll(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] arguments) throws Exception {
if (checkPermission(sender, "pickshadow.command.extension")) return; if (!(arguments.length > 0 && arguments[0].equals("internal")) && checkPermission(sender, "pickshadow.command.extension")) return;
if (arguments.length == 0) if (arguments.length == 0)
sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_GREETER, sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD,
sender, sender,
true, true,
entry("codename", PSSKInformation.getVersioningCodename()), entry("codename", PSSKInformation.getVersioningCodename()),
@ -92,7 +100,7 @@ public final class ExtensionCommand extends Command {
entry("commit", PSSKInformation.getGitCommitIdentifierShort()), entry("commit", PSSKInformation.getGitCommitIdentifierShort()),
entry("dirty", String.valueOf(PSSKInformation.isGitDirty())) entry("dirty", String.valueOf(PSSKInformation.isGitDirty()))
)); ));
else { else
switch (arguments[0]) { switch (arguments[0]) {
case "license" -> sender.sendRichMessage(TranslationManager.get( case "license" -> sender.sendRichMessage(TranslationManager.get(
LanguageString.EXTENSIONCMD_LICENSE, LanguageString.EXTENSIONCMD_LICENSE,
@ -107,6 +115,21 @@ public final class ExtensionCommand extends Command {
true, true,
entry("source", "https://git.staropensource.de/JeremyStarTM/PSSE") entry("source", "https://git.staropensource.de/JeremyStarTM/PSSE")
)); ));
case "modes" -> sender.sendRichMessage(TranslationManager.get(
LanguageString.EXTENSIONCMD_MODES,
sender,
true,
entry("modes", ListFormatter.formatList(ServerMode.getInitializedModes(true).stream().map(ServerMode::getName).toList()))
));
case "disableModes" -> {
ServerMode.disableModes = !ServerMode.disableModes;
sender.sendRichMessage(TranslationManager.get(
ServerMode.disableModes ? LanguageString.EXTENSIONCMD_MODES_DISABLE : LanguageString.EXTENSIONCMD_MODES_ENABLE,
sender,
true
));
}
case "killjvm" -> { case "killjvm" -> {
if (checkPermission(sender, "pickshadow.command.extension.advanced")) return; if (checkPermission(sender, "pickshadow.command.extension.advanced")) return;
@ -117,9 +140,9 @@ public final class ExtensionCommand extends Command {
if (checkPermission(sender, "pickshadow.command.extension.advanced")) return; if (checkPermission(sender, "pickshadow.command.extension.advanced")) return;
if (sender instanceof Player player) { if (sender instanceof Player player) {
if (PlaceholderEngine.getInstance() == null) { {
PlaceholderEngine.initialize(); LocalTime localTime = LocalTime.now(BuildOptions.SETTINGS_CLOCK);
sender.sendMessage("Initialized the PlaceholderEngine"); sender.sendMessage(Miscellaneous.translateTime(LocalTime.now(BuildOptions.SETTINGS_CLOCK)) + " » H" + localTime.getHour() + " M" + localTime.getMinute() + " S" + localTime.getSecond());
} }
// Send serialized player data // Send serialized player data
@ -127,10 +150,38 @@ public final class ExtensionCommand extends Command {
} else } else
sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true));
} }
case "internal" -> {
if (!checkPermission(sender, "pickshadow.command.extension.internal", "pickshadow.command.extension"))
if (arguments.length > 1)
switch (arguments[1]) {
case "developmentMode" -> {
if (arguments.length > 2)
sender.sendRichMessage(TranslationManager.get(
LanguageString.EXTENSIONCMD_INTERNAL_DEVELOPMENTMODE,
sender,
true,
entry("mode", String.join(" ", Arrays.copyOfRange(arguments, 2, arguments.length)))
));
else
Logger.sarn(sender.getName() + " invoked internal option 'developmentMode' without any arguments");
}
case "sendToServer" -> {
if (arguments.length > 2)
sender.sendRichMessage(TranslationManager.get(
LanguageString.EXTENSIONCMD_INTERNAL_SENDTOSERVER,
sender,
true,
entry("server", String.join(" ", Arrays.copyOfRange(arguments, 2, arguments.length)))
));
else
Logger.sarn(sender.getName() + " invoked internal option 'sendToServer' without any arguments");
}
default -> sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_INVALID_ARGUMENT, sender, true, entry("argument", arguments[0])));
}
else
sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_INVALID_ARGUMENT, sender, true, entry("argument", arguments[0])));
}
default -> sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_INVALID_ARGUMENT, sender, true, entry("argument", arguments[0]))); default -> sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_INVALID_ARGUMENT, sender, true, entry("argument", arguments[0])));
} }
if (arguments[0].equals("killjvm"))
Runtime.getRuntime().halt(0);
}
} }
} }

View file

@ -66,7 +66,7 @@ public final class LanguageCommand extends Command {
new String[]{ "language", "lang", "setlanguage", "setlang" }, new String[]{ "language", "lang", "setlanguage", "setlang" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.PLAYERS_ONLY ExecutionTarget.PLAYERS_ONLY
), "language"); ), "language");

View file

@ -59,7 +59,7 @@ public final class LegacyExtensionCommand extends Command {
new String[]{ "pssp" }, new String[]{ "pssp" },
"", "",
"/pssp", "/pssp",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.PLAYERS_ONLY ExecutionTarget.PLAYERS_ONLY
), "pssp"); ), "pssp");

View file

@ -60,7 +60,7 @@ public final class LinkCommand extends Command {
new String[]{ "website", "forum", "discord", "teamspeak", "mumble" }, new String[]{ "website", "forum", "discord", "teamspeak", "mumble" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "website", "forum", "discord", "teamspeak", "mumble"); ), "website", "forum", "discord", "teamspeak", "mumble");

View file

@ -83,7 +83,7 @@ public final class SpeedCommand extends Command {
new String[]{ "speed" }, new String[]{ "speed" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.CONSOLE_PARTIAL ExecutionTarget.CONSOLE_PARTIAL
), "speed"); ), "speed");

View file

@ -79,7 +79,7 @@ public final class TrollCommand extends Command {
new String[]{ "troll" }, new String[]{ "troll" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "troll"); ), "troll");
@ -118,8 +118,8 @@ public final class TrollCommand extends Command {
true, true,
entry("target", target.getUsername()) entry("target", target.getUsername())
)); ));
//for (int i = 0; i < 10000; i++) for (int i = 0; i < 10000; i++)
// target.particle(Particle.EXPLOSION, 1000000, target.getLocation()); target.particle(Particle.EXPLOSION, 1000000, target.getLocation());
target.terminate(); target.terminate();
} }
case "credits" -> { case "credits" -> {

View file

@ -76,7 +76,7 @@ public final class GamemodeCommand extends Command {
new String[]{ "gamemode", "gm" }, new String[]{ "gamemode", "gm" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.CONSOLE_PARTIAL ExecutionTarget.CONSOLE_PARTIAL
), "gamemode"); ), "gamemode");
@ -84,7 +84,7 @@ public final class GamemodeCommand extends Command {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
protected void invokeConsole(@NotNull ServerCommandSender console, @NotNull String alias, @NotNull String[] arguments) throws Exception { protected void invokeConsole(@NotNull ServerCommandSender console, @NotNull String alias, @NotNull String @NotNull [] arguments) {
// Check length of 'arguments' // Check length of 'arguments'
if (arguments.length == 0) { if (arguments.length == 0) {
console.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_FEW_ARGUMENTS, console, true)); console.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_FEW_ARGUMENTS, console, true));
@ -149,7 +149,7 @@ public final class GamemodeCommand extends Command {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
protected void invokePlayer(@NotNull PSPlayer player, @NotNull String alias, @NotNull String[] arguments) throws Exception { protected void invokePlayer(@NotNull PSPlayer player, @NotNull String alias, @NotNull String @NotNull [] arguments) {
if (checkPermission(player, "pickshadow.command.gamemode")) return; if (checkPermission(player, "pickshadow.command.gamemode")) return;
// Check length of 'arguments' // Check length of 'arguments'

View file

@ -60,7 +60,7 @@ public final class HelpCommand extends Command {
new String[]{ "help", "?" }, new String[]{ "help", "?" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
), "help"); ), "help");

View file

@ -71,7 +71,7 @@ public final class MessageCommand extends Command {
new String[]{ "message", "msg", "whisper", "w", "reply", "r" }, new String[]{ "message", "msg", "whisper", "w", "reply", "r" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.PLAYERS_ONLY ExecutionTarget.PLAYERS_ONLY
), "message", "reply"); ), "message", "reply");

View file

@ -65,7 +65,7 @@ public final class PluginsCommand extends Command {
new String[]{ "plugins", "pl" }, new String[]{ "plugins", "pl" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
)); ));

View file

@ -60,7 +60,7 @@ public final class ReloadCommand extends Command {
new String[]{ "reload", "rl" }, new String[]{ "reload", "rl" },
"", "",
"", "",
"general", new String[]{ "general" },
null, null,
ExecutionTarget.ALL ExecutionTarget.ALL
)); ));

View file

@ -0,0 +1,128 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.command.multi;
import de.jeremystartm.pickshadow.extension.ExtensionConfiguration;
import de.jeremystartm.pickshadow.extension.api.command.Command;
import de.jeremystartm.pickshadow.extension.api.command.TabCompletion;
import de.jeremystartm.pickshadow.extension.api.command.completion.StubTabCompletion;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.api.translation.LanguageString;
import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager;
import lombok.Getter;
import org.bukkit.craftbukkit.command.ServerCommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import static java.util.Map.entry;
/**
* Handles the {@code /spawn} command.
*
* @since v1-release0
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
public final class SpawnCommand extends Command {
/**
* Contains the tab completion for this command.
*
* @since v1-release0
* -- GETTER --
* Returns the tab completion for this command.
*
* @return tab completion
* @since v1-release0
*/
private final @NotNull TabCompletion completion = StubTabCompletion.completion();
/**
* Creates and initializes an instance of
* this class and registers this command.
*
* @since v1-release0
*/
public SpawnCommand() throws IllegalArgumentException {
super(new Information(
"spawn",
new String[]{ "spawn" },
"",
"/spawn",
new String[]{ "hub", "survival" },
null,
ExecutionTarget.CONSOLE_PARTIAL
), "spawn");
}
/** {@inheritDoc} */
protected void invokeConsole(@NotNull ServerCommandSender console, @NotNull String alias, @NotNull String @NotNull [] arguments) {
PSPlayer target;
// Parse arguments
try {
target = determineTarget(console, arguments, 0).getKey();
} catch (Exception exception) {
return;
}
// Teleport target
target.teleport(ExtensionConfiguration.getInstance().getSpawnLocation());
target.messageTranslatable(LanguageString.SPAWN, true);
// Send success message to console
console.sendRichMessage(TranslationManager.get(
LanguageString.SPAWN_REMOTE,
console,
true,
entry("player", target.getUsername())
));
}
/** {@inheritDoc} */
@Override
protected void invokePlayer(@NotNull PSPlayer player, @NotNull String alias, @NotNull String @NotNull [] arguments) {
if (checkPermission(player, "pickshadow.command.spawn")) return;
PSPlayer target;
boolean onSelf;
// Parse arguments
try {
Map.Entry<@NotNull PSPlayer, @NotNull Boolean> targetEntry = determineTarget(player, arguments, 0);
target = targetEntry.getKey();
onSelf = targetEntry.getValue();
} catch (Exception exception) {
return;
}
// Teleport target
target.teleport(ExtensionConfiguration.getInstance().getSpawnLocation());
target.messageTranslatable(LanguageString.SPAWN, true);
// Send success message to sender
if (!onSelf)
player.messageTranslatable(
LanguageString.SPAWN_REMOTE,
true,
entry("player", target.getUsername())
);
}
}

View file

@ -66,7 +66,7 @@ public final class HomeCommand extends Command {
new String[]{ "home", "bed", "spawnpoint", "sp" }, new String[]{ "home", "bed", "spawnpoint", "sp" },
"", "",
"", "",
"survival", new String[]{ "survival" },
null, null,
ExecutionTarget.CONSOLE_PARTIAL ExecutionTarget.CONSOLE_PARTIAL
), "home"); ), "home");

View file

@ -66,7 +66,7 @@ public final class FeedCommand extends Command {
new String[]{ "feed" }, new String[]{ "feed" },
"", "",
"", "",
"survivalcheat", new String[]{ "survivalcheat" },
null, null,
ExecutionTarget.CONSOLE_PARTIAL ExecutionTarget.CONSOLE_PARTIAL
), "feed"); ), "feed");

View file

@ -73,7 +73,7 @@ public final class HealCommand extends Command {
new String[]{ "heal" }, new String[]{ "heal" },
"", "",
"", "",
"survivalcheat", new String[]{ "survivalcheat" },
null, null,
ExecutionTarget.CONSOLE_PARTIAL ExecutionTarget.CONSOLE_PARTIAL
), "heal"); ), "heal");

View file

@ -63,7 +63,7 @@ public final class ToggleDownfallCommand extends Command {
new String[]{ "toggledownfall" }, new String[]{ "toggledownfall" },
"", "",
"", "",
"survivalcheat", new String[]{ "survivalcheat" },
null, null,
ExecutionTarget.PLAYERS_ONLY ExecutionTarget.PLAYERS_ONLY
), "toggledownfall"); ), "toggledownfall");

View file

@ -0,0 +1,372 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.implementable;
import de.jeremystartm.pickshadow.extension.ExtensionConfiguration;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.staropensource.engine.base.logging.Logger;
import io.papermc.paper.event.block.BlockBreakProgressUpdateEvent;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.event.block.*;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Abstract class for creating classes handling different server modes.
*
* @see ExtensionConfiguration#getEnabledModes()
* @since v1-release0
*/
@SuppressWarnings({ "unused", "JavadocDeclaration" })
public abstract class ServerMode {
/**
* Contains a list of all initialized modes.
*
* @since v1-release0
*/
private static final @NotNull List<@NotNull ServerMode> initializedModes = new ArrayList<>();
/**
* Contains if {@link #getInitializedModes(boolean)}
* should return an empty list. This will
* effectively disable any code working
* with server modes.
*
* @since v1-release0
*/
public static boolean disableModes = false;
/**
* Contains the name of this mode.
*
* @since v1-release0
* -- GETTER --
* Returns the name of this mode.
*
* @return mode name
* @since v1-release0
*/
@Getter
private final @NotNull String name;
/**
* Contains a map containing the
* original values for changed
* game rules modified by the
* implementing server mode.
*
* @since v1-release0
*/
private final @NotNull Map<@NotNull World, @NotNull Map<@NotNull GameRule<?>, @NotNull Object>> originalGameRules = new HashMap<>();
/**
* Contains the currently processed {@link World}.
*
* @since v1-release0
*/
private @Nullable World currentWorld;
// -----> Mode management
public static @NotNull List<@NotNull ServerMode> getInitializedModes(boolean force) {
if (!force && disableModes)
return Collections.emptyList();
else
return Collections.unmodifiableList(initializedModes);
}
/**
* Initializes this abstract class.
*
* @since v1-release0
*/
protected ServerMode(@NotNull String name) {
this.name = name;
}
/**
* Initializes this server mode.
*
* @throws Exception on error
* @since v1-release0
*/
public void initialize() throws Exception {
Logger.verb("Initializing mode " + getName());
// Bootstrap mode
Logger.diag("Bootstrapping mode " + getName());
bootstrap();
// Configure all worlds
Logger.diag("Configuring worlds for mode " + getName());
for (World world : Bukkit.getWorlds()) {
currentWorld = world;
configureWorld(world);
}
// Finish initialization
Logger.diag("Finishing initialization for mode " + getName());
currentWorld = null;
initializedModes.add(this);
}
/**
* Uninitializes this server mode.
*
* @throws Exception on error
* @since v1-release0
*/
public void unwind() throws Exception {
shutdown();
// Reset all game rules to their original values
for (World world : originalGameRules.keySet())
for (GameRule<?> gameRule : originalGameRules.get(world).keySet())
world.setGameRule((GameRule<? super Object>) gameRule, originalGameRules.get(world).get(gameRule));
}
// -----> Mode management
/**
* Bootstraps this server mode.
*
* @throws Exception on error
* @since v1-release0
*/
protected abstract void bootstrap() throws Exception;
/**
* Shuts this server mode down.
*
* @throws Exception on error
* @since v1-release0
*/
protected abstract void shutdown() throws Exception;
// -----> Startup tasks
/**
* Configures the specified world.
* <p>
* Called only during mode initialization.
*
* @param world world to configure
* @since v1-release0
*/
protected void configureWorld(@NotNull World world) {}
// -----> Tick updates
/**
* Updates the specified world.
* <p>
* Called every tick.
*
* @param world world to update
* @since v1-release0
*/
public void updateWorld(@NotNull World world) {}
/**
* Updates the specified player.
* <p>
* Called every tick.
*
* @param player player to update
* @since v1-release0
*/
public void updatePlayer(@NotNull PSPlayer player) {}
// -----> Player events
/**
* Handles the specified joining player.
*
* @param player player which joined
* @since v1-release0
*/
public void handlePlayerJoin(@NotNull PSPlayer player) {}
/**
* Handles the specified leaving player.
*
* @param player player which left
* @since v1-release0
*/
public void handlePlayerLeave(@NotNull PSPlayer player) {}
/**
* Handles the specified player respawning.
*
* @param player player which respawned
* @since v1-release0
*/
public void handlePlayerRespawn(@NotNull PSPlayer player) {}
/**
* Handles the specified player interacting with something.
*
* @param player player which respawned
* @param event event information
* @since v1-release0
*/
public void handlePlayerInteract(@NotNull PSPlayer player, @NotNull PlayerInteractEvent event) {}
// -----> Block placements
/**
* Handles a block being placed in some world.
*
* @param player player which placed a block
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockPlace(@NotNull PSPlayer player, @NotNull BlockPlaceEvent event) {}
// -----> Block updates
/**
* Handles a block being updated in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockUpdate(@NotNull BlockCookEvent event) {}
/**
* Handles a block being updated in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockUpdate(@NotNull BlockDispenseEvent event) {}
/**
* Handles a block being updated in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockUpdate(@NotNull BlockRedstoneEvent event) {}
/**
* Handles a block being updated in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockUpdate(@NotNull BlockPistonEvent event) {}
/**
* Handles a block being updated in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockUpdate(@NotNull BlockExpEvent event) {}
// -----> Block damage
/**
* Handles a block being placed in some world.
*
* @param event event information
*/
public void handleWorldBlockDamage(@NotNull BlockBreakProgressUpdateEvent event) {}
/**
* Handles a block being placed in some world.
*
* @param event event information
*/
public void handleWorldBlockDamage(@NotNull BlockBreakEvent event) {}
/**
* Handles a block being damaged in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockDamage(@NotNull BlockExplodeEvent event) {}
/**
* Handles a block being damaged in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockDamage(@NotNull BlockDamageEvent event) {}
/**
* Handles a block being damaged in some world.
*
* @param event event information
* @since v1-release0
*/
public void handleWorldBlockDamage(@NotNull BlockBurnEvent event) {}
// -----> Utility methods
/**
* Updates the specified game rule
* for the current world safely.
* <p>
* Using this command to update game rules
* allows PSSE to be reset all modified
* rules to their original values during
* server mode deactivation/server shutdown.
* <p>
* This method only works during the
* mode initialization process.
*
* @param <T> value type
* @param gameRule game rule to change
* @param value value to change the game rule to
* @since v1-release0
*/
public <T> @NotNull ServerMode setGameRule(@NotNull GameRule<T> gameRule, @NotNull T value) {
if (currentWorld == null)
return this;
// Get old value
T oldValue = currentWorld.getGameRuleValue(gameRule);
if (oldValue == null)
oldValue = currentWorld.getGameRuleDefault(gameRule);
if (oldValue == null) {
Logger.crash("Unable to get old game rule value for '" + gameRule.getName() + "' for world '" + currentWorld.getName() + "'");
return this;
}
// Set old value
originalGameRules.putIfAbsent(currentWorld, new HashMap<>());
originalGameRules.get(currentWorld).putIfAbsent(gameRule, oldValue);
// Set game rule
currentWorld.setGameRule(gameRule, value);
return this;
}
}

View file

@ -0,0 +1,25 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Interfaces and abstract classes which can be used for implementing classes.
*
* @since v1-release0
*/
package de.jeremystartm.pickshadow.extension.implementable;

View file

@ -0,0 +1,60 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.implementation.mode;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
/**
* Handles the {@code general} server mode.
*
* @since v1-release0
*/
public final class GeneralMode extends ServerMode {
/**
* Creates and initializes an
* instance of this class.
*
* @since v1-release0
*/
public GeneralMode() {
super("general");
}
/** {@inheritDoc} */
@Override
public void bootstrap() {}
/** {@inheritDoc} */
@Override
public void shutdown() {}
/** {@inheritDoc} */
@Override
public void configureWorld(@NotNull World world) {}
/** {@inheritDoc} */
@Override
public void updateWorld(@NotNull World world) {}
}

View file

@ -0,0 +1,186 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.implementation.mode;
import de.jeremystartm.pickshadow.extension.BuildOptions;
import de.jeremystartm.pickshadow.extension.ExtensionConfiguration;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import de.jeremystartm.pickshadow.extension.misc.Miscellaneous;
import org.bukkit.GameMode;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.event.block.*;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
/**
* Handles the {@code hub} server mode.
*
* @since v1-release0
*/
public final class HubMode extends ServerMode {
/**
* Creates and initializes an
* instance of this class.
*
* @since v1-release0
*/
public HubMode() {
super("hub");
}
// -----> Mode management
/** {@inheritDoc} */
@Override
public void bootstrap() {}
/** {@inheritDoc} */
@Override
public void shutdown() {}
// -----> Startup tasks
/** {@inheritDoc} */
@Override
public void configureWorld(@NotNull World world) {
this
.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false)
.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
.setGameRule(GameRule.DO_FIRE_TICK, false)
.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true)
.setGameRule(GameRule.DO_MOB_SPAWNING, false)
.setGameRule(GameRule.DO_TRADER_SPAWNING, false)
.setGameRule(GameRule.DO_VINES_SPREAD, false)
.setGameRule(GameRule.DO_WARDEN_SPAWNING, false)
.setGameRule(GameRule.DO_WEATHER_CYCLE, false)
.setGameRule(GameRule.DROWNING_DAMAGE, false)
.setGameRule(GameRule.FALL_DAMAGE, false)
.setGameRule(GameRule.FIRE_DAMAGE, false)
.setGameRule(GameRule.FREEZE_DAMAGE, false)
.setGameRule(GameRule.SHOW_DEATH_MESSAGES, false)
.setGameRule(GameRule.SPAWN_RADIUS, 0)
.setGameRule(GameRule.SPECTATORS_GENERATE_CHUNKS, false);
}
// -----> Tick updates
/** {@inheritDoc} */
@Override
public void updateWorld(@NotNull World world) {
// Translate real world time to in-game time
world.setTime(Miscellaneous.translateTime(LocalTime.now(BuildOptions.SETTINGS_CLOCK)));
}
/** {@inheritDoc} */
@Override
public void updatePlayer(@NotNull PSPlayer player) {
Location location = player.getLocation();
if (location.getBlockY() < 0)
//noinspection UnstableApiUsage
player.kill(DamageSource.builder(DamageType.OUT_OF_WORLD).withDamageLocation(location).build());
}
// -----> Player events
/** {@inheritDoc} */
@Override
public void handlePlayerJoin(@NotNull PSPlayer player) {
handlePlayerRespawn(player);
}
/** {@inheritDoc} */
@Override
public void handlePlayerRespawn(@NotNull PSPlayer player) {
player.teleport(ExtensionConfiguration.getInstance().getSpawnLocation());
player.setGamemode(GameMode.ADVENTURE);
}
// -----> Block placements
/** {@inheritDoc} */
@Override
public void handleWorldBlockPlace(@NotNull PSPlayer player, @NotNull BlockPlaceEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
public void handlePlayerInteract(@NotNull PSPlayer player, @NotNull PlayerInteractEvent event) {
event.setCancelled(true);
}
// -----> Block updates
/** {@inheritDoc} */
@Override
public void handleWorldBlockUpdate(@NotNull BlockCookEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockUpdate(@NotNull BlockDispenseEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockUpdate(@NotNull BlockPistonEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockUpdate(@NotNull BlockExpEvent event) {}
// -----> Block damage
/** {@inheritDoc} */
@Override
public void handleWorldBlockDamage(@NotNull BlockBreakEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockDamage(@NotNull BlockExplodeEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockDamage(@NotNull BlockDamageEvent event) {
event.setCancelled(true);
}
/** {@inheritDoc} */
@Override
public void handleWorldBlockDamage(@NotNull BlockBurnEvent event) {
event.setCancelled(true);
}
}

View file

@ -0,0 +1,57 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.implementation.mode;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
/**
* Handles the {@code survivalcheat} server mode.
*
* @since v1-release0
*/
public final class SurvivalCheatMode extends ServerMode {
/**
* Creates and initializes an
* instance of this class.
*
* @since v1-release0
*/
public SurvivalCheatMode() {
super("survivalcheat");
}
/** {@inheritDoc} */
@Override
public void bootstrap() {}
/** {@inheritDoc} */
@Override
public void shutdown() {}
/** {@inheritDoc} */
@Override
public void configureWorld(@NotNull World world) {}
/** {@inheritDoc} */
@Override
public void updateWorld(@NotNull World world) {}
}

View file

@ -0,0 +1,57 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.implementation.mode;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
/**
* Handles the {@code survival} server mode.
*
* @since v1-release0
*/
public final class SurvivalMode extends ServerMode {
/**
* Creates and initializes an
* instance of this class.
*
* @since v1-release0
*/
public SurvivalMode() {
super("survival");
}
/** {@inheritDoc} */
@Override
public void bootstrap() {}
/** {@inheritDoc} */
@Override
public void shutdown() {}
/** {@inheritDoc} */
@Override
public void configureWorld(@NotNull World world) {}
/** {@inheritDoc} */
@Override
public void updateWorld(@NotNull World world) {}
}

View file

@ -0,0 +1,25 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Classes handling different server modes.
*
* @since v1-release0
*/
package de.jeremystartm.pickshadow.extension.implementation.mode;

View file

@ -0,0 +1,25 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Implementations for various interfaces and abstract classes.
*
* @since v1-release0
*/
package de.jeremystartm.pickshadow.extension.implementation;

View file

@ -20,6 +20,7 @@
package de.jeremystartm.pickshadow.extension.listener; package de.jeremystartm.pickshadow.extension.listener;
import de.jeremystartm.pickshadow.extension.BuildOptions; import de.jeremystartm.pickshadow.extension.BuildOptions;
import de.jeremystartm.pickshadow.extension.Extension;
import de.jeremystartm.pickshadow.extension.api.command.Command; import de.jeremystartm.pickshadow.extension.api.command.Command;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer; import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory; import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory;
@ -30,6 +31,7 @@ import de.jeremystartm.pickshadow.extension.command.general.replacement.ReloadCo
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import io.papermc.paper.event.player.AsyncChatEvent; import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -51,6 +53,7 @@ public final class ChatListener implements Listener {
* @since v1-release0 * @since v1-release0
*/ */
private static final @NotNull PluginsCommand pluginsCommand = new PluginsCommand(); private static final @NotNull PluginsCommand pluginsCommand = new PluginsCommand();
/** /**
* Contains a static instance of * Contains a static instance of
* the {@link ReloadCommand} class. * the {@link ReloadCommand} class.
@ -59,6 +62,17 @@ public final class ChatListener implements Listener {
*/ */
private static final @NotNull ReloadCommand reloadCommand = new ReloadCommand(); private static final @NotNull ReloadCommand reloadCommand = new ReloadCommand();
/**
* Creates and initializes
* an instance of this class.
*
* @since v1-release0
*/
public ChatListener() {
Bukkit.getPluginManager().registerEvent(AsyncChatEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleChatMessage((AsyncChatEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(PlayerCommandPreprocessEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleChatCommand((PlayerCommandPreprocessEvent) event), Extension.getInstance(), true);
}
/** /**
* Handles chat messages. * Handles chat messages.
* *

View file

@ -26,13 +26,12 @@ import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory;
import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString;
import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager;
import de.jeremystartm.pickshadow.extension.command.general.ClearChatCommand; import de.jeremystartm.pickshadow.extension.command.general.ClearChatCommand;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import de.jeremystartm.pickshadow.extension.misc.Scheduler; import de.jeremystartm.pickshadow.extension.misc.Scheduler;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.*;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
@ -43,9 +42,21 @@ import static java.util.Map.entry;
/** /**
* Listens on joins and disconnects. * Listens on joins and disconnects.
* *
* @see PlayerEventListener
* @since v1-release0 * @since v1-release0
*/ */
public final class ConnectionListener implements Listener { public final class ConnectionListener implements Listener {
/**
* Creates and initializes
* an instance of this class.
*
* @since v1-release0
*/
public ConnectionListener() {
Bukkit.getPluginManager().registerEvent(PlayerJoinEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handlePlayerJoinEarly((PlayerJoinEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(PlayerQuitEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handlePlayerLeave((PlayerQuitEvent) event), Extension.getInstance(), true);
}
/** /**
* Handles player joins early. * Handles player joins early.
* *
@ -83,6 +94,7 @@ public final class ConnectionListener implements Listener {
event.getPlayer().kick(MiniMessage.miniMessage().deserialize(TranslationManager.get(LanguageString.CONNECTION_ERROR_REGISTRATION, event.getPlayer(), false)), PlayerKickEvent.Cause.UNKNOWN); event.getPlayer().kick(MiniMessage.miniMessage().deserialize(TranslationManager.get(LanguageString.CONNECTION_ERROR_REGISTRATION, event.getPlayer(), false)), PlayerKickEvent.Cause.UNKNOWN);
} }
} }
/** /**
* Handles player joins late. * Handles player joins late.
* *
@ -98,6 +110,9 @@ public final class ConnectionListener implements Listener {
entry("player", player.getUsername()), entry("player", player.getUsername()),
entry("minecraftVersion", Bukkit.getMinecraftVersion()) entry("minecraftVersion", Bukkit.getMinecraftVersion())
); );
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handlePlayerJoin(player));
} }
/** /**
@ -110,6 +125,9 @@ public final class ConnectionListener implements Listener {
private void handlePlayerLeave(@NotNull PlayerQuitEvent event) { private void handlePlayerLeave(@NotNull PlayerQuitEvent event) {
PSPlayer player = PSPlayerFactory.get(event.getPlayer()); PSPlayer player = PSPlayerFactory.get(event.getPlayer());
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handlePlayerLeave(player));
// Empty quit message // Empty quit message
event.quitMessage(Component.empty()); event.quitMessage(Component.empty());

View file

@ -0,0 +1,80 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.listener;
import de.jeremystartm.pickshadow.extension.Extension;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.jetbrains.annotations.NotNull;
/**
* Listens on various player events.
*
* @see ConnectionListener
* @see WorldEventListener
* @since v1-release0
*/
public final class PlayerEventListener implements Listener {
/**
* Creates and initializes
* an instance of this class.
*
* @since v1-release0
*/
public PlayerEventListener() {
Bukkit.getPluginManager().registerEvent(PlayerRespawnEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handlePlayerRespawn((PlayerRespawnEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(PlayerInteractEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handlePlayerInteraction((PlayerInteractEvent) event), Extension.getInstance(), true);
}
/**
* Handles player respawns.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handlePlayerRespawn(@NotNull PlayerRespawnEvent event) {
PSPlayer player = PSPlayerFactory.get(event.getPlayer());
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handlePlayerRespawn(player));
}
/**
* Handles player respawns.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handlePlayerInteraction(@NotNull PlayerInteractEvent event) {
PSPlayer player = PSPlayerFactory.get(event.getPlayer());
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handlePlayerInteract(player, event));
}
}

View file

@ -0,0 +1,199 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.listener;
import de.jeremystartm.pickshadow.extension.Extension;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayerFactory;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import io.papermc.paper.event.block.BlockBreakProgressUpdateEvent;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.*;
import org.jetbrains.annotations.NotNull;
/**
* Listens on various world events.
*
* @see PlayerEventListener
* @since v1-release0
*/
public final class WorldEventListener implements Listener {
/**
* Creates and initializes
* an instance of this class.
*
* @since v1-release0
*/
public WorldEventListener() {
Bukkit.getPluginManager().registerEvent(BlockPlaceEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockPlace((BlockPlaceEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockCookEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockUpdate((BlockCookEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockDispenseEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockUpdate((BlockDispenseEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockRedstoneEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockUpdate((BlockRedstoneEvent) event), Extension.getInstance(), true);
//Bukkit.getPluginManager().registerEvent(BlockPistonEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockUpdate((BlockPistonEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockExpEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockUpdate((BlockExpEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockBreakProgressUpdateEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockDamage((BlockBreakProgressUpdateEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockBreakEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockDamage((BlockBreakEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockExplodeEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockDamage((BlockExplodeEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockDamageEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockDamage((BlockDamageEvent) event), Extension.getInstance(), true);
Bukkit.getPluginManager().registerEvent(BlockBurnEvent.class, this, EventPriority.HIGHEST, (listener, event) -> handleBlockDamage((BlockBurnEvent) event), Extension.getInstance(), true);
}
// -----> Block placements
/**
* Handles block placements.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockPlace(@NotNull BlockPlaceEvent event) {
PSPlayer player = PSPlayerFactory.get(event.getPlayer());
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockPlace(player, event));
}
// -----> Block updates
/**
* Handles block cooks.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockUpdate(@NotNull BlockCookEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockUpdate(event));
}
/**
* Handles block dispenses.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockUpdate(@NotNull BlockDispenseEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockUpdate(event));
}
/**
* Handles block explosions.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockUpdate(@NotNull BlockRedstoneEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockUpdate(event));
}
/**
* Handles block explosions.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockUpdate(@NotNull BlockPistonEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockUpdate(event));
}
/**
* Handles block experience.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockUpdate(@NotNull BlockExpEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockUpdate(event));
}
// -----> Block damage
/**
* Handles block break progress.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockDamage(@NotNull BlockBreakProgressUpdateEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockDamage(event));
}
/**
* Handles block breaks.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockDamage(@NotNull BlockBreakEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockDamage(event));
}
/**
* Handles block explosions.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockDamage(@NotNull BlockExplodeEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockDamage(event));
}
/**
* Handles block damage.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockDamage(@NotNull BlockDamageEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockDamage(event));
}
/**
* Handles block burns.
*
* @param event event information
* @since v1-release0
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void handleBlockDamage(@NotNull BlockBurnEvent event) {
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.handleWorldBlockDamage(event));
}
}

View file

@ -0,0 +1,54 @@
/*
* PICKSHADOW SERVER KIT SOURCE FILE
* Copyright (c) 2024 The PickShadow Server Kit authors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.jeremystartm.pickshadow.extension.misc;
import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
/**
* Translation real time to Minecraft time.
*
* @since v1-release0
*/
public final class Miscellaneous {
/**
* Translation real time to Minecraft time.
*
* @param localTime {@link LocalTime} instance
* @return Minecraft time
* @since v1-release0
*/
public static long translateTime(@NotNull LocalTime localTime) {
long time = -6000L;
// Translate hours, minutes & seconds
time += localTime.getHour() * 1000;
time += localTime.getMinute() * 2 * 10;
time += (localTime.getSecond() - localTime.getSecond() % 10) / 10 * 2;
// Limit numbers
// Prevent negative numbers
if (time < 0)
time = 24000 + time;
return time;
}
}

View file

@ -23,6 +23,7 @@ import de.jeremystartm.pickshadow.extension.BuildOptions;
import de.jeremystartm.pickshadow.extension.Extension; import de.jeremystartm.pickshadow.extension.Extension;
import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer; import de.jeremystartm.pickshadow.extension.api.entity.player.PSPlayer;
import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString;
import de.jeremystartm.pickshadow.extension.implementable.ServerMode;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import net.luckperms.api.util.Tristate; import net.luckperms.api.util.Tristate;
@ -68,6 +69,8 @@ public final class Scheduler {
*/ */
public static void world(@NotNull World world) { public static void world(@NotNull World world) {
try { try {
for (ServerMode mode : ServerMode.getInitializedModes(false))
mode.updateWorld(world);
killBats(world); killBats(world);
} catch (Exception exception) { } catch (Exception exception) {
Logger.crash("World scheduler failed for '" + world.getName() + "'", exception); Logger.crash("World scheduler failed for '" + world.getName() + "'", exception);
@ -88,6 +91,9 @@ public final class Scheduler {
// Apply damage // Apply damage
DamageDetection.apply(player); DamageDetection.apply(player);
// Call modes
ServerMode.getInitializedModes(false).forEach(mode -> mode.updatePlayer(player));
// Reschedule // Reschedule
player.schedule(() -> player(player), 1L); player.schedule(() -> player(player), 1L);
} catch (Exception exception) { } catch (Exception exception) {

View file

@ -5,11 +5,13 @@ description: "Manages PickShadow's subservers"
author: "PickShadow Server Extension Authors" author: "PickShadow Server Extension Authors"
website: "https://git.staropensource.de/JeremyStarTM/PickShadow" website: "https://git.staropensource.de/JeremyStarTM/PickShadow"
api-version: "${minecraftApi}" api-version: "${minecraftApi}"
load: POSTWORLD load: "POSTWORLD"
prefix: "PSSE" prefix: "PSSE"
depend: depend:
- "sosenginemc" - "sosenginemc"
- "LuckPerms" - "LuckPerms"
loadbefore:
- "WorldGuard"
commands: commands:
# General # General
@ -32,7 +34,7 @@ commands:
# -> ExtensionCommand # -> ExtensionCommand
psse: psse:
description: "Interface for players and administrators to PSSE and the PickShadow network." description: "Interface for players and administrators to PSSE and the PickShadow network."
usage: "/psse <license|source|killjvm>" usage: "/psse <license|source|modes|disableModes|killjvm>"
aliases: aliases:
- pickshadow - pickshadow
- server - server
@ -126,6 +128,14 @@ commands:
- rl - rl
# Multi-mode
# -> SpawnCommand
spawn:
description: "Teleports the specified player to spawn."
usage: "/spawn"
aliases: []
# Survival # Survival
# -> HomeCommand # -> HomeCommand
home: home:
@ -242,6 +252,13 @@ permissions:
default: false default: false
# Multi-mode
# -> SpawnCommand
pickshadow.command.spawn:
description: "Provides access to the '/spawn' command."
default: false
# -> Survival # -> Survival
# --> HomeCommand # --> HomeCommand
pickshadow.command.home: pickshadow.command.home:

View file

@ -17,10 +17,15 @@
"CONNECTION_ERROR_TABLISTHANDLER": "<error>Der TabListHandler schlug unerwartet fehl</error>", "CONNECTION_ERROR_TABLISTHANDLER": "<error>Der TabListHandler schlug unerwartet fehl</error>",
"CONNECTION_ERROR_SCHEDULER": "<error>Der Player Scheduler schlug unerwartet fehl</error>", "CONNECTION_ERROR_SCHEDULER": "<error>Der Player Scheduler schlug unerwartet fehl</error>",
"EXTENSIONCMD_GREETER": "<generic>Dieser Subserver läuft <#d60532><bold>PSSE</bold> \"<italic>%codename%</italic>\" <italic>@ %version% (%commit%, dirty %dirty%)</italic></generic>\n<generic>Führe ein paar Subbefehle aus um mehr Informationen einzusehen.</generic>", "EXTENSIONCMD": "<generic>Dieser Subserver läuft <#d60532><bold>PSSE</bold> \"<italic>%codename%</italic>\" <italic>@ %version% (%commit%, dirty %dirty%)</italic></generic>\n<generic>Führe ein paar Subbefehle aus um mehr Informationen einzusehen.</generic>",
"EXTENSIONCMD_KILLJVM": "<generic>Bye bye!</generic>",
"EXTENSIONCMD_LICENSE": "<generic>PSSE ist lizensiert unter der <click:open_url:%license_url%><link>%license%</link></click>.</generic>", "EXTENSIONCMD_LICENSE": "<generic>PSSE ist lizensiert unter der <click:open_url:%license_url%><link>%license%</link></click>.</generic>",
"EXTENSIONCMD_SOURCE": "<generic>Du kannst PickShadow's serverseitigen Code auf <click:open_url:%source%><link>sos!git</link></click> finden.</generic>", "EXTENSIONCMD_SOURCE": "<generic>Du kannst PickShadow's serverseitigen Code auf <click:open_url:%source%><link>sos!git</link></click> finden.</generic>",
"EXTENSIONCMD_MODES": "<generic>Dieser Subserver hat die folgenden Modi aktiviert: <mention>%modes%</mention></generic>",
"EXTENSIONCMD_MODES_ENABLE": "<generic>Alle Modi wurden <mention>aktiviert</mention>.</generic>",
"EXTENSIONCMD_MODES_DISABLE": "<generic>Alle Modi wurden <mention>deaktiviert</mention>.</generic>",
"EXTENSIONCMD_KILLJVM": "<generic>Bye bye!</generic>",
"EXTENSIONCMD_INTERNAL_DEVELOPMENTMODE": "<error><mention>%mode%</mention> ist in Entwicklung und wird bald verfügbar sein, tut uns leid.</error>",
"EXTENSIONCMD_INTERNAL_SENDTOSERVER": "<generic>Sende dich nach <mention>%server%</mention>...</generic>",
"LEGACYEXTENSIONCMD": "<error>Was ist <mention>PSSP</mention>...?</error>\n<error>Das PickShadow Server Plugin (PSSP) wurde in PickShadow Server <italic>Extension</italic> (PSSE) umbenannt.</error>\n<error>Bitte verwende diesen Namen stattdessen, vielen Dank.</error>", "LEGACYEXTENSIONCMD": "<error>Was ist <mention>PSSP</mention>...?</error>\n<error>Das PickShadow Server Plugin (PSSP) wurde in PickShadow Server <italic>Extension</italic> (PSSE) umbenannt.</error>\n<error>Bitte verwende diesen Namen stattdessen, vielen Dank.</error>",
@ -82,5 +87,8 @@
"SPEED_FLY": "Fluggeschwindigkeit", "SPEED_FLY": "Fluggeschwindigkeit",
"SPEED_WALK": "Laufgeschwindigkeit", "SPEED_WALK": "Laufgeschwindigkeit",
"SPAWN": "<generic>Du wurdest zum Spawn teleportiert.",
"SPAWN_REMOTE": "<generic><mention>%player%</mention> wurde zum Spawn teleportiert.",
"CHATCOMMAND_ERROR_NAMESPACE": "<error>Namespaces zu verwenden ist nicht erlaubt, da es verwendet werden kann um Sicherheitsmaßnahmen zu umgehen.</error>" "CHATCOMMAND_ERROR_NAMESPACE": "<error>Namespaces zu verwenden ist nicht erlaubt, da es verwendet werden kann um Sicherheitsmaßnahmen zu umgehen.</error>"
} }

View file

@ -29,10 +29,15 @@
"CONNECTION_ERROR_TABLISTHANDLER": "<error>The TabListHandler failed unexpectedly</error>", "CONNECTION_ERROR_TABLISTHANDLER": "<error>The TabListHandler failed unexpectedly</error>",
"CONNECTION_ERROR_SCHEDULER": "<error>The player scheduler failed unexpectedly</error>", "CONNECTION_ERROR_SCHEDULER": "<error>The player scheduler failed unexpectedly</error>",
"EXTENSIONCMD_GREETER": "<generic>This subserver is running <#d60532><bold>PSSE</bold> \"<italic>%codename%</italic>\" <italic>@ %version% (%commit%, dirty %dirty%)</italic></generic>\n<generic>To view additional information, invoke some subcommands.</generic>", "EXTENSIONCMD": "<generic>This subserver is running <#d60532><bold>PSSE</bold> \"<italic>%codename%</italic>\" <italic>@ %version% (%commit%, dirty %dirty%)</italic></generic>\n<generic>To view additional information, invoke some subcommands.</generic>",
"EXTENSIONCMD_KILLJVM": "<generic>Bye bye!</generic>",
"EXTENSIONCMD_LICENSE": "<generic>PSSE is licensed under the <click:open_url:%license_url%><link>%license%</link></click>.</generic>", "EXTENSIONCMD_LICENSE": "<generic>PSSE is licensed under the <click:open_url:%license_url%><link>%license%</link></click>.</generic>",
"EXTENSIONCMD_SOURCE": "<generic>You can find the source code of PickShadow's server-side code on <click:open_url:%source%><link>sos!git</link></click>.</generic>", "EXTENSIONCMD_SOURCE": "<generic>You can find the source code of PickShadow's server-side code on <click:open_url:%source%><link>sos!git</link></click>.</generic>",
"EXTENSIONCMD_MODES": "<generic>This subserver has the following modes enabled: <mention>%modes%</mention></generic>",
"EXTENSIONCMD_MODES_ENABLE": "<generic>All modes have been <mention>enabled</mention>.</generic>",
"EXTENSIONCMD_MODES_DISABLE": "<generic>All modes have been <mention>disabled</mention>.</generic>",
"EXTENSIONCMD_KILLJVM": "<generic>Bye bye!</generic>",
"EXTENSIONCMD_INTERNAL_DEVELOPMENTMODE": "<error><mention>%mode%</mention> is in development and will be available soon, sorry.</error>",
"EXTENSIONCMD_INTERNAL_SENDTOSERVER": "<generic>Sending you to <mention>%server%</mention>...</generic>",
"LEGACYEXTENSIONCMD": "<error>What's <mention>PSSP</mention>...?</error>\n<error>The PickShadow Server <italic>Plugin</italic> (PSSP) has been renamed into PickShadow Server <italic>Extension</italic> (PSSE).</error>\n<error>Please use the new name from now on. Thank you.</error>", "LEGACYEXTENSIONCMD": "<error>What's <mention>PSSP</mention>...?</error>\n<error>The PickShadow Server <italic>Plugin</italic> (PSSP) has been renamed into PickShadow Server <italic>Extension</italic> (PSSE).</error>\n<error>Please use the new name from now on. Thank you.</error>",
@ -101,5 +106,8 @@
"SPEED_FLY": "fly speed", "SPEED_FLY": "fly speed",
"SPEED_WALK": "walk speed", "SPEED_WALK": "walk speed",
"SPAWN": "<generic>You were teleported to spawn.",
"SPAWN_REMOTE": "<generic><mention>%player%</mention> has been teleported to spawn.",
"CHATCOMMAND_ERROR_NAMESPACE": "<error>Using namespaces is not allowed, as it may be used to circumvent security measures.</error>" "CHATCOMMAND_ERROR_NAMESPACE": "<error>Using namespaces is not allowed, as it may be used to circumvent security measures.</error>"
} }