Compare commits

...

9 Commits

Author SHA1 Message Date
Len b68835918f Implement CuffFeature 2025-06-29 14:36:59 +02:00
Len d3cd059af1 Implement FlightFeature 2025-06-29 13:35:57 +02:00
Len 612a0281d6 Implement GodModeFeature 2025-06-29 13:33:21 +02:00
Len d4f33b8c2e Implement EssentiaFeatures 2025-06-29 13:01:37 +02:00
Len 9fa43c8c65 Add Timer.java 2025-06-29 12:50:51 +02:00
Len 13a78515c5 Switch to use Cosmos 2025-06-29 08:31:59 +02:00
Len 91e6d3be8e Inline logging in Config.java 2025-06-29 08:28:07 +02:00
Len 06a8aa0f65 Implement base for RTP 2024-10-20 14:22:45 +02:00
Len 139c51e9b8 Make plugin load postworld 2024-10-15 21:38:46 +02:00
24 changed files with 735 additions and 82 deletions

View File

@ -1,5 +1,6 @@
package com.alttd.essentia; package com.alttd.essentia;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
public interface EssentiaAPI { public interface EssentiaAPI {
@ -19,4 +20,6 @@ public interface EssentiaAPI {
Provider.instance = instance; Provider.instance = instance;
} }
} }
public void addLocationValidator(LocationValidator locationValidator);
} }

View File

@ -0,0 +1,8 @@
package com.alttd.essentia.api.model.randomteleport;
import org.bukkit.Location;
public interface LocationValidator {
boolean validate(Location location);
}

View File

@ -5,7 +5,7 @@ plugins {
dependencies { dependencies {
implementation(project(":api")) implementation(project(":api"))
compileOnly("com.alttd:Galaxy-API:1.21.1-R0.1-SNAPSHOT") compileOnly("com.alttd.cosmos:cosmos-api:1.21.6-R0.1-SNAPSHOT")
api("org.reflections:reflections:0.10.2") api("org.reflections:reflections:0.10.2")
compileOnly("org.projectlombok:lombok:1.18.34") compileOnly("org.projectlombok:lombok:1.18.34")

View File

@ -2,11 +2,15 @@ package com.alttd.essentia;
import com.alttd.essentia.commands.EssentiaCommand; import com.alttd.essentia.commands.EssentiaCommand;
import com.alttd.essentia.configuration.Config; import com.alttd.essentia.configuration.Config;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import com.alttd.essentia.feature.Features;
import com.alttd.essentia.model.annotations.Depends;
import com.alttd.essentia.storage.StorageManager; import com.alttd.essentia.storage.StorageManager;
import com.alttd.essentia.storage.StorageProvider; import com.alttd.essentia.storage.StorageProvider;
import com.alttd.essentia.storage.StorageType; import com.alttd.essentia.storage.StorageType;
import com.alttd.essentia.user.EssentiaUserManager; import com.alttd.essentia.user.EssentiaUserManager;
import com.alttd.essentia.api.user.UserManager; import com.alttd.essentia.api.user.UserManager;
import com.alttd.essentia.util.Timer;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
@ -18,6 +22,8 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.reflections.Reflections; import org.reflections.Reflections;
import org.reflections.scanners.Scanners; import org.reflections.scanners.Scanners;
import java.util.ArrayList;
import java.util.List;
import java.util.Set; import java.util.Set;
public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI { public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
@ -27,10 +33,15 @@ public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
@Getter @Getter
private UserManager userManager; private UserManager userManager;
@Getter
private List<LocationValidator> locationValidators;
@Getter @Getter
private StorageProvider storageProvider; private StorageProvider storageProvider;
@Getter
private Features features;
@Override @Override
public void onLoad() { public void onLoad() {
instance = this; instance = this;
@ -39,11 +50,15 @@ public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
@Override @Override
public void onEnable() { public void onEnable() {
loadConfiguration(); new Timer("onEnable", () -> {
loadCommands(); new Timer("loadConfiguration", this::loadConfiguration);
loadEventListeners(); new Timer("loadFeatures", this::loadFeatures);
loadManagers(); new Timer("loadCommands", this::loadCommands);
loadStorageProvider(); new Timer("loadEventListeners", this::loadEventListeners);
new Timer("loadManagers", this::loadManagers);
new Timer("loadStorageProvider", this::loadStorageProvider);
new Timer("loadLocationValidators", this::loadLocationValidators);
});
} }
@Override @Override
@ -57,6 +72,11 @@ public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
Config.init(); Config.init();
} }
public void loadFeatures() {
features = new Features(this);
features.registerAll();
}
void loadCommands() { void loadCommands() {
Reflections reflections = new Reflections("com.alttd.essentia.commands"); Reflections reflections = new Reflections("com.alttd.essentia.commands");
Set<Class<?>> subTypes = reflections.get(Scanners.SubTypes.of(EssentiaCommand.class).asClass()); Set<Class<?>> subTypes = reflections.get(Scanners.SubTypes.of(EssentiaCommand.class).asClass());
@ -67,6 +87,13 @@ public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
subTypes.forEach(clazz -> { subTypes.forEach(clazz -> {
try { try {
EssentiaCommand essentiaCommand = (EssentiaCommand) clazz.getDeclaredConstructor().newInstance(); EssentiaCommand essentiaCommand = (EssentiaCommand) clazz.getDeclaredConstructor().newInstance();
final Depends depends = clazz.getAnnotation(Depends.class);
if (depends != null) {
if (!features.isEnabled(depends.value())) {
return;
}
}
commands.register(essentiaCommand.command(), essentiaCommand.description(), essentiaCommand.aliases()); commands.register(essentiaCommand.command(), essentiaCommand.description(), essentiaCommand.aliases());
} catch (Exception e) { } catch (Exception e) {
EssentiaPlugin.instance().getLogger().severe("Failed to register command " + clazz.getSimpleName()); EssentiaPlugin.instance().getLogger().severe("Failed to register command " + clazz.getSimpleName());
@ -100,4 +127,26 @@ public class EssentiaPlugin extends JavaPlugin implements EssentiaAPI {
storageProvider.startAutoSaving(); storageProvider.startAutoSaving();
} }
void loadLocationValidators() {
this.locationValidators = new ArrayList<>();
Reflections reflections = new Reflections("com.alttd.essentia.feature.randomteleport.validator");
Set<Class<?>> subTypes = reflections.get(Scanners.SubTypes.of(LocationValidator.class).asClass());
subTypes.forEach(clazz -> {
try {
LocationValidator locationValidator = (LocationValidator) clazz.getDeclaredConstructor().newInstance();
addLocationValidator(locationValidator);
} catch (Exception e) {
EssentiaPlugin.instance().getLogger().severe("Failed to add locationValidator " + clazz.getSimpleName());
EssentiaPlugin.instance().getLogger().severe(e.getMessage());
}
});
}
// TODO -- make this reload proof for API
@Override
public void addLocationValidator(LocationValidator locationValidator) {
locationValidators.add(locationValidator);
}
} }

View File

@ -0,0 +1,58 @@
package com.alttd.essentia.commands.admin;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.commands.EssentiaCommand;
import com.alttd.essentia.request.RandomTeleportRequest;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class RandomTeleportCommand implements EssentiaCommand {
@Override
public String commandName() {
return "randomteleport";
}
@Override
public @NotNull LiteralCommandNode<CommandSourceStack> command() {
final LiteralArgumentBuilder<CommandSourceStack> builder =
Commands.literal(commandName())
.requires(commandSourceStack -> commandSourceStack.getSender().hasPermission(adminCommandPermission()))
.then(
Commands.argument("player", ArgumentTypes.player())
.requires(commandSourceStack -> commandSourceStack.getSender().hasPermission(adminOtherCommandPermission()))
.executes((source) -> {
CommandSourceStack sourceStack = source.getSource();
Player target = source.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(sourceStack).getFirst();
execute(source.getSource().getSender(), target);
return 1;
})
);
return builder.build();
}
@Override
public String description() {
return "Teleport a player to a random location!";
}
public void execute(CommandSender sender, Player target) { // TODO - messages
TagResolver placeholders = TagResolver.resolver(
Placeholder.component("requester", sender.name()),
Placeholder.component("target", target.displayName())
);
new RandomTeleportRequest(EssentiaPlugin.instance(), target, target);
}
}

View File

@ -2,6 +2,8 @@ package com.alttd.essentia.configuration;
import com.alttd.essentia.EssentiaPlugin; import com.alttd.essentia.EssentiaPlugin;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
@ -34,7 +36,7 @@ public class Config {
config.load(CONFIG_FILE); config.load(CONFIG_FILE);
} catch (IOException ignore) { } catch (IOException ignore) {
} catch (InvalidConfigurationException ex) { } catch (InvalidConfigurationException ex) {
EssentiaPlugin.instance().getLogger().log(Level.SEVERE, "Could not load config.yml, please correct your syntax errors", ex); log(Level.SEVERE, "Could not load config.yml, please correct your syntax errors", ex);
Throwables.throwIfUnchecked(ex); Throwables.throwIfUnchecked(ex);
} }
config.options().header(HEADER); config.options().header(HEADER);
@ -43,7 +45,7 @@ public class Config {
version = getInt("config-version", 1); version = getInt("config-version", 1);
set("config-version", 1); set("config-version", 1);
EssentiaPlugin.instance().getLogger().info("Essentia Configuration loaded!"); log(Level.INFO,"Essentia Configuration loaded!");
readConfig(Config.class, null); readConfig(Config.class, null);
} }
@ -57,7 +59,7 @@ public class Config {
} catch (InvocationTargetException ex) { } catch (InvocationTargetException ex) {
Throwables.throwIfUnchecked(ex); Throwables.throwIfUnchecked(ex);
} catch (Exception ex) { } catch (Exception ex) {
EssentiaPlugin.instance().getLogger().log(Level.SEVERE, "Error invoking " + method, ex); log(Level.SEVERE, "Error invoking " + method, ex);
} }
} }
} }
@ -69,7 +71,7 @@ public class Config {
try { try {
config.save(CONFIG_FILE); config.save(CONFIG_FILE);
} catch (IOException ex) { } catch (IOException ex) {
EssentiaPlugin.instance().getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
} }
} }
@ -107,6 +109,10 @@ public class Config {
EssentiaPlugin.instance().getLogger().log(level, s); EssentiaPlugin.instance().getLogger().log(level, s);
} }
protected static void log(Level level, String s, Throwable thrown) {
EssentiaPlugin.instance().getLogger().log(level, s, thrown);
}
public static int TELEPORT_REQUEST_TIMEOUT = 30; public static int TELEPORT_REQUEST_TIMEOUT = 30;
public static boolean TELEPORT_REQUEST_TIMEOUT_MESSAGES = true; public static boolean TELEPORT_REQUEST_TIMEOUT_MESSAGES = true;
public static boolean BACK_ON_DEATH = false; public static boolean BACK_ON_DEATH = false;
@ -270,4 +276,10 @@ public class Config {
MYSQL_PASSWORD = getString("storage.mysql.password", MYSQL_PASSWORD); MYSQL_PASSWORD = getString("storage.mysql.password", MYSQL_PASSWORD);
} }
public static Object2BooleanMap<String> enabledFeatures = new Object2BooleanOpenHashMap<>();
private static void features() {
enabledFeatures.clear();
enabledFeatures.defaultReturnValue(true);
}
} }

View File

@ -0,0 +1,19 @@
package com.alttd.essentia.feature;
public interface EssentiaFeature {
String featureName();
void register();
boolean isEnabled();
default void start() {}
default void stop() {}
default void reload() {
stop();
start();
}
}

View File

@ -0,0 +1,57 @@
package com.alttd.essentia.feature;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.configuration.Config;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import java.util.*;
import java.util.logging.Level;
public class Features {
private final EssentiaPlugin plugin;
private final Map<String, EssentiaFeature> features;
public Features(EssentiaPlugin plugin) {
this.plugin = plugin;
features = new HashMap<>();
}
public boolean isEnabled(String featureName) {
if (features.containsKey(featureName)) {
return features.get(featureName).isEnabled();
}
return false;
}
public void registerAll() {
Reflections reflections = new Reflections("com.alttd.essentia.feature");
Set<Class<?>> subTypes = reflections.get(Scanners.SubTypes.of(EssentiaFeature.class).asClass());
subTypes.forEach(clazz -> {
try {
EssentiaFeature essentiaFeature = (EssentiaFeature) clazz.getDeclaredConstructor().newInstance();
if (!Config.enabledFeatures.getBoolean(essentiaFeature.featureName())) {
return;
}
essentiaFeature.register();
essentiaFeature.reload();
features.putIfAbsent(essentiaFeature.featureName(), essentiaFeature);
} catch (Exception e) {
log(Level.SEVERE,"Failed to register feature " + clazz.getSimpleName(), e);
}
});
}
protected void log(Level level, String s) {
plugin.getLogger().log(level, s);
}
protected void log(Level level, String s, Throwable thrown) {
plugin.getLogger().log(level, s, thrown);
}
}

View File

@ -0,0 +1,34 @@
package com.alttd.essentia.feature.cuff;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.feature.EssentiaFeature;
import org.bukkit.plugin.PluginManager;
public class CuffFeature implements EssentiaFeature {
private final EssentiaPlugin plugin;
private boolean enabled = false;
public CuffFeature() {
this.plugin = EssentiaPlugin.instance();
}
@Override
public String featureName() {
return "cuff";
}
@Override
public void register() {
final PluginManager pluginManager = plugin.getServer().getPluginManager();
pluginManager.registerEvents(new CuffListener(), plugin);
this.enabled = true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}

View File

@ -1,4 +1,4 @@
package com.alttd.essentia.listeners; package com.alttd.essentia.feature.cuff;
import com.alttd.essentia.EssentiaPlugin; import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.api.user.User; import com.alttd.essentia.api.user.User;

View File

@ -0,0 +1,35 @@
package com.alttd.essentia.feature.flight;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.feature.EssentiaFeature;
import org.bukkit.plugin.PluginManager;
public class FlightFeature implements EssentiaFeature {
private final EssentiaPlugin plugin;
private boolean enabled = false;
public FlightFeature() {
this.plugin = EssentiaPlugin.instance();
}
@Override
public String featureName() {
return "flight";
}
@Override
public void register() {
final PluginManager pluginManager = plugin.getServer().getPluginManager();
pluginManager.registerEvents(new FlightListener(), plugin);
this.enabled = true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}

View File

@ -1,4 +1,4 @@
package com.alttd.essentia.listeners; package com.alttd.essentia.feature.flight;
import com.alttd.essentia.EssentiaPlugin; import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.api.model.UserSettings; import com.alttd.essentia.api.model.UserSettings;
@ -8,7 +8,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerChangedWorldEvent;
// TODO -- set flight and flying when player logs in!
public class FlightListener implements Listener { public class FlightListener implements Listener {
private final EssentiaPlugin plugin; private final EssentiaPlugin plugin;

View File

@ -0,0 +1,49 @@
package com.alttd.essentia.feature.godmode;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.api.model.UserSettings;
import com.alttd.essentia.api.user.User;
import com.alttd.essentia.api.user.UserManager;
import com.alttd.essentia.feature.EssentiaFeature;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
public class GodModeFeature implements EssentiaFeature {
private final EssentiaPlugin plugin;
private boolean enabled = false;
public GodModeFeature() {
this.plugin = EssentiaPlugin.instance();
}
@Override
public String featureName() {
return "GodMode";
}
@Override
public void register() {
final PluginManager pluginManager = plugin.getServer().getPluginManager();
pluginManager.registerEvents(new GodModeListener(this), plugin);
this.enabled = true;
}
@Override
public boolean isEnabled() {
return enabled;
}
protected boolean isGodModeEnabled(Player player) {
UserManager userManager = plugin.userManager();
User user = userManager.getUser(player);
if (user == null)
return false;
UserSettings userSettings = user.getUserSettings();
return userSettings.godMode();
}
}

View File

@ -0,0 +1,114 @@
package com.alttd.essentia.feature.godmode;
import com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent;
import org.bukkit.entity.LivingEntity;
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.*;
public class GodModeListener implements Listener {
private GodModeFeature godModeFeature;
public GodModeListener(GodModeFeature godModeFeature) {
this.godModeFeature = godModeFeature;
}
@EventHandler
public void onFoodLevelChange(FoodLevelChangeEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
@EventHandler
public void onPhantomPreSpawn(PhantomPreSpawnEvent event) {
if (!(event.getSpawningEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
event.setShouldAbortSpawn(true);
}
@EventHandler
public void onEntityTargetLivingEntity(EntityTargetLivingEntityEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
@EventHandler
public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
@EventHandler
public void onPotionSplashEvent(PotionSplashEvent event) {
for (LivingEntity entity : event.getAffectedEntities()) {
if (!(entity instanceof Player player))
continue;
if (!isGodModeEnabled(player))
return;
event.setIntensity(player, 0f);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onEntityCombustByEntityEvent(EntityCombustByEntityEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityCombust(final EntityCombustEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityDamage(final EntityDamageEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!isGodModeEnabled(player))
return;
event.setCancelled(true);
}
private boolean isGodModeEnabled(Player player) {
return godModeFeature.isGodModeEnabled(player);
}
}

View File

@ -0,0 +1,29 @@
package com.alttd.essentia.feature.randomteleport;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import org.bukkit.Location;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import java.util.HashSet;
import java.util.Set;
// TODO - load biomes from config
public class BiomeValidator implements LocationValidator {
private final boolean whitelist;
private final Set<Biome> biomes;
public BiomeValidator() {
this.biomes = new HashSet<>();
this.whitelist = false;
this.biomes.add(Biome.OCEAN);
}
@Override
public boolean validate(Location location) {
Block block = location.getBlock();
return biomes.contains(block.getBiome()) == whitelist;
}
}

View File

@ -0,0 +1,28 @@
package com.alttd.essentia.feature.randomteleport;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.util.HashSet;
import java.util.Set;
// TODO - load block list from config
public class BlockValidator implements LocationValidator {
private final boolean whitelist;
private final Set<Material> materials;
public BlockValidator() {
materials = new HashSet<>();
this.whitelist = false;
}
@Override
public boolean validate(Location location) {
Block block = location.getBlock();
return materials.contains(block.getType()) == whitelist;
}
}

View File

@ -0,0 +1,14 @@
package com.alttd.essentia.feature.randomteleport;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import org.bukkit.Location;
// TODO - claim hooks for GP. LandClaims should hook into the plugin and add the validator.
public class ProtectionValidator implements LocationValidator {
@Override
public boolean validate(Location location) {
return false;
}
}

View File

@ -0,0 +1,13 @@
package com.alttd.essentia.feature.randomteleport;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import org.bukkit.Location;
public class WorldBorderValidator implements LocationValidator {
@Override
public boolean validate(Location location) {
return location.getWorld().getWorldBorder().isInside(location);
}
}

View File

@ -1,67 +0,0 @@
package com.alttd.essentia.listeners;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.api.model.UserSettings;
import com.alttd.essentia.api.user.User;
import com.alttd.essentia.api.user.UserManager;
import com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
public class GodModeListener implements Listener {
private final EssentiaPlugin plugin;
public GodModeListener() {
this.plugin = EssentiaPlugin.instance();
}
@EventHandler
public void onFoodLevelChange(FoodLevelChangeEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!hasGodMode(player))
return;
event.setCancelled(true);
}
@EventHandler
public void onPhantomPreSpawn(PhantomPreSpawnEvent event) {
if (!(event.getSpawningEntity() instanceof Player player))
return;
if (!hasGodMode(player))
return;
event.setCancelled(true);
event.setShouldAbortSpawn(true);
}
@EventHandler
public void onEntityTargetLivingEntity(EntityTargetLivingEntityEvent event) {
if (!(event.getEntity() instanceof Player player))
return;
if (!hasGodMode(player))
return;
event.setCancelled(true);
}
private boolean hasGodMode(Player player) {
UserManager userManager = plugin.userManager();
User user = userManager.getUser(player);
if (user == null)
return false;
UserSettings userSettings = user.getUserSettings();
return userSettings.godMode();
}
}

View File

@ -0,0 +1,12 @@
package com.alttd.essentia.model.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Depends {
String value();
}

View File

@ -12,10 +12,10 @@ import org.bukkit.entity.Player;
public abstract class EssentiaRequest implements Request { public abstract class EssentiaRequest implements Request {
private final EssentiaPlugin plugin; protected final EssentiaPlugin plugin;
@Getter private final Player requester; @Getter private final Player requester;
@Getter private final Player target; @Getter private final Player target;
private final RequestTimeout timeoutTask; @Getter private final RequestTimeout timeoutTask;
TagResolver placeholders; TagResolver placeholders;

View File

@ -0,0 +1,168 @@
package com.alttd.essentia.request;
import com.alttd.essentia.EssentiaPlugin;
import com.alttd.essentia.api.model.randomteleport.LocationValidator;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
public class RandomTeleportRequest extends EssentiaRequest {
private static final List<int[]> CHUNK_POS = new ArrayList<>();
private final Multimap<Integer, Integer> checked = MultimapBuilder.hashKeys().hashSetValues().build();
private final Random random;
private final Location center; // todo - config
private final int minRadius; // todo - config
private final int maxRadius; // todo - config
private final int maxTries = 100; // TODO - config
private int checks;
CompletableFuture<Location> future;
static {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
CHUNK_POS.add(new int[]{x, z});
}
}
}
public RandomTeleportRequest(EssentiaPlugin plugin, Player requester, Player target) {
super(plugin, requester, target);
this.random = new Random();
this.center = Bukkit.getWorlds().getFirst().getSpawnLocation(); // todo - config and sanity check for worlds
this.minRadius = 50;
this.maxRadius = 20000;
this.timeoutTask().cancel();
this.teleport();
}
/**
* Start searching for a safe random location within the world
*
* @return A CompletableFuture for when the searching is finished.
*/
public CompletableFuture<Location> startSearch() {
future = new CompletableFuture<>();
target().sendRichMessage("Running search task");
plugin.getServer().getScheduler().runTask(plugin, () -> searchRandomLocation(future));
return future;
}
// needs to be a task
private void searchRandomLocation(CompletableFuture<Location> future) {
if (future.isCancelled() || future.isDone() || future.isCompletedExceptionally()) {
return;
}
Location randomLoc = center.clone();
int minChunk = minRadius >> 4;
int maxChunk = maxRadius >> 4;
int randChunkX;
int randChunkZ;
do {
checks++;
if (checks >= maxTries) {
future.completeExceptionally(new Exception("Could not find random location within %s tries".formatted(maxTries + "")));
return;
}
randChunkX = (random.nextBoolean() ? 1 : -1) * random.nextInt(maxChunk + 1);
randChunkZ = (random.nextBoolean() ? 1 : -1) * random.nextInt(maxChunk + 1);
target().sendRichMessage("randChunkX: " + randChunkX + ", randChunkZ: " + randChunkZ);
} while (!checked.put(randChunkX, randChunkZ)
|| !inRadius(Math.abs(randChunkX), Math.abs(randChunkZ), minChunk, maxChunk)
/*|| randomLoc.getWorld().isChunkGenerated(randChunkX, randChunkZ)*/);
randomLoc.setX(((center.getBlockX() >> 4) + randChunkX) * 16);
randomLoc.setZ(((center.getBlockZ() >> 4) + randChunkZ) * 16);
randomLoc.getWorld().getChunkAtAsync(randomLoc).thenApply(chunk -> {
if (chunk == null) {
searchRandomLocation(future);
return false;
}
chunk.addPluginChunkTicket(plugin);
try {
int indexOffset = random.nextInt(CHUNK_POS.size());
Location foundLoc = null;
for (int i = 0; i < CHUNK_POS.size(); i++) {
int index = (i + indexOffset) % CHUNK_POS.size();
boolean validated = true;
Location loc = randomLoc.clone().add(CHUNK_POS.get(index)[0], 0, CHUNK_POS.get(index)[1]);
if (!inRadius(loc)) {
continue;
}
for (LocationValidator validator : plugin.locationValidators()) {
if (!validator.validate(loc)) {
validated = false;
break;
}
}
if (validated) {
foundLoc = loc;
break;
}
}
if (foundLoc != null) {
// all checks are for the top block, put we want a location above that so add 1 to y
future.complete(foundLoc.add(0, 1, 0));
return true;
}
plugin.getServer().getScheduler().runTaskLater(plugin, () -> searchRandomLocation(future), 1);
return false;
} finally {
chunk.removePluginChunkTicket(plugin);
}
}).exceptionally(future::completeExceptionally);
}
private boolean inRadius(Location location) {
int diffX = Math.abs(location.getBlockX() - center.getBlockX());
int diffZ = Math.abs(location.getBlockZ() - center.getBlockZ());
return inRadius(diffX, diffZ, minRadius, maxRadius);
}
private boolean inRadius(int diffX, int diffZ, int minRadius, int maxRadius) {
return diffX >= minRadius && diffX <= maxRadius && diffZ <= maxRadius
|| diffZ >= minRadius && diffZ <= maxRadius && diffX <= maxRadius;
}
@Override
protected void teleport() {
startSearch().thenApply(randomLocation -> {
randomLocation.setX(randomLocation.getBlockX() + 0.5);
randomLocation.setY(randomLocation.getY() + 0.5);
randomLocation.setZ(randomLocation.getBlockZ() + 0.5);
target().teleportAsync(randomLocation)
.whenComplete((success, exception) -> {
target().sendMessage("You have been teleported to a random location."); // todo -- config
});
return true;
});
if (!target().isOnline() || !requester().isOnline()) {
cancel();
return;
}
}
@Override
public void cancel() {
try {
timeoutTask().cancel();
} catch (IllegalStateException ignore) {
}
}
}

View File

@ -0,0 +1,17 @@
package com.alttd.essentia.util;
import com.alttd.essentia.EssentiaPlugin;
public class Timer {
public Timer(String id, Runnable runnable) {
long startTime = System.currentTimeMillis();
runnable.run();
long duration = System.currentTimeMillis() - startTime;
EssentiaPlugin.instance().getLogger().info("Loading " + id + " took " + duration + "ms");
}
}

View File

@ -2,6 +2,7 @@ name: Essentia
version: ${version} version: ${version}
main: com.alttd.essentia.EssentiaPlugin main: com.alttd.essentia.EssentiaPlugin
description: Altitude essentials ;) description: Altitude essentials ;)
load: POSTWORLD
authors: authors:
- destro174 - destro174
api-version: "1.21" api-version: "1.21"