make everything not static, it makes the api much easier to make

This commit is contained in:
Pyrbu 2023-05-16 14:46:11 +01:00
parent 1aeae3f12a
commit a08e973319
74 changed files with 796 additions and 507 deletions

View file

@ -51,6 +51,7 @@ shadowJar {
relocate "space.arim.dazzleconf", "lol.pyr.znpcsplus.lib.dazzleconf"
relocate "lol.pyr.director", "lol.pyr.znpcsplus.lib.command"
relocate "lol.pyr.serviceinjector", "lol.pyr.znpcsplus.lib.serviceinjector"
minimize()
}

View file

@ -1,12 +0,0 @@
package lol.pyr.znpcsplus;
import lol.pyr.znpcsplus.api.ZApi;
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
public class ZNpcsApi implements ZApi {
@Override
public NpcRegistry getNpcRegistry() {
return NpcRegistryImpl.get();
}
}

View file

@ -1,13 +1,17 @@
package lol.pyr.znpcsplus;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import lol.pyr.director.adventure.command.CommandManager;
import lol.pyr.director.adventure.command.MultiCommand;
import lol.pyr.director.adventure.parse.primitive.BooleanParser;
import lol.pyr.director.adventure.parse.primitive.IntegerParser;
import lol.pyr.znpcsplus.api.ZApi;
import lol.pyr.znpcsplus.api.ZApiProvider;
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
import lol.pyr.znpcsplus.commands.*;
import lol.pyr.znpcsplus.commands.hologram.*;
import lol.pyr.znpcsplus.commands.parsers.EntityPropertyParser;
@ -16,72 +20,70 @@ import lol.pyr.znpcsplus.commands.parsers.NpcEntryParser;
import lol.pyr.znpcsplus.commands.parsers.NpcTypeParser;
import lol.pyr.znpcsplus.commands.storage.LoadAllCommand;
import lol.pyr.znpcsplus.commands.storage.SaveAllCommand;
import lol.pyr.znpcsplus.config.Configs;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.interaction.InteractionPacketListener;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.metadata.*;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcTypeImpl;
import lol.pyr.znpcsplus.packets.*;
import lol.pyr.znpcsplus.scheduling.FoliaScheduler;
import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.cache.SkinCache;
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
import lol.pyr.znpcsplus.tasks.NpcVisibilityTask;
import lol.pyr.znpcsplus.updater.UpdateChecker;
import lol.pyr.znpcsplus.updater.UpdateNotificationListener;
import lol.pyr.znpcsplus.user.User;
import lol.pyr.znpcsplus.user.UserListener;
import lol.pyr.znpcsplus.user.UserManager;
import lol.pyr.znpcsplus.util.BungeeUtil;
import lol.pyr.znpcsplus.util.FoliaUtil;
import lol.pyr.znpcsplus.util.LazyLoader;
import lol.pyr.znpcsplus.util.ZLocation;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.commons.io.FileUtils;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import java.util.HashMap;
public class ZNpcsPlus extends JavaPlugin {
public class ZNpcsPlus extends JavaPlugin implements ZApi {
private static final int PLUGIN_ID = 18244;
public static boolean PLACEHOLDERS_SUPPORTED;
public static Logger LOGGER;
public static File PLUGIN_FOLDER;
public static File PATH_FOLDER;
private TaskScheduler scheduler;
private BukkitAudiences adventure;
private SkinCache skinCache;
public static TaskScheduler SCHEDULER;
public static BungeeUtil BUNGEE_UTIL;
public static BukkitAudiences ADVENTURE;
public static LegacyComponentSerializer LEGACY_AMPERSAND_SERIALIZER = LegacyComponentSerializer.builder()
private MetadataFactory metadataFactory;
private NpcRegistryImpl npcRegistry;
private UserManager userManager;
private final LegacyComponentSerializer textSerializer = LegacyComponentSerializer.builder()
.character('&')
.hexCharacter('#')
.hexColors().build();
private PacketEventsAPI<Plugin> packetEvents;
private boolean enabled = false;
public static final String DEBUG_NPC_PREFIX = "debug_npc";
public static void debug(String str) {
if (!Configs.config().debugEnabled()) return;
LOGGER.info("[DEBUG] " + str);
}
@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().getSettings().checkForUpdates(false);
PacketEvents.getAPI().load();
LOGGER = getLogger();
PLUGIN_FOLDER = getDataFolder();
PATH_FOLDER = new File(PLUGIN_FOLDER, "paths");
packetEvents = SpigotPacketEventsBuilder.build(this);
PacketEvents.setAPI(packetEvents);
packetEvents.getSettings().checkForUpdates(false);
packetEvents.load();
}
private void log(String str) {
@ -95,71 +97,71 @@ public class ZNpcsPlus extends JavaPlugin {
log(ChatColor.YELLOW + " /__ | \\| | |__ .__) " + ChatColor.GOLD + " | " + ChatColor.GRAY + "Maintained with " + ChatColor.RED + "\u2764 " + ChatColor.GRAY + " by Pyr#6969");
log("");
if (Bukkit.getPluginManager().isPluginEnabled("ServersNPC")) {
PluginManager pluginManager = Bukkit.getPluginManager();
if (pluginManager.isPluginEnabled("ServersNPC")) {
log(ChatColor.DARK_RED + " * Detected old version of ZNPCs! Disabling the plugin.");
log("");
Bukkit.getPluginManager().disablePlugin(this);
pluginManager.disablePlugin(this);
return;
}
long before = System.currentTimeMillis();
File oldFolder = new File(PLUGIN_FOLDER.getParent(), "ServersNPC");
if (!PLUGIN_FOLDER.exists() && oldFolder.exists()) {
log(ChatColor.WHITE + " * Converting old ZNPCs files...");
try {
FileUtils.moveDirectory(oldFolder, PLUGIN_FOLDER);
} catch (IOException e) {
log(ChatColor.RED + " * Failed to convert old ZNPCs files" + (e.getMessage() == null ? "" : " due to " + e.getMessage()));
}
}
log(ChatColor.WHITE + " * Initializing Adventure...");
ADVENTURE = BukkitAudiences.create(this);
adventure = BukkitAudiences.create(this);
log(ChatColor.WHITE + " * Initializing PacketEvents...");
PacketEvents.getAPI().getEventManager().registerListener(new InteractionPacketListener(), PacketListenerPriority.MONITOR);
PacketEvents.getAPI().init();
packetEvents.getEventManager().registerListener(new InteractionPacketListener(userManager, npcRegistry), PacketListenerPriority.MONITOR);
packetEvents.init();
PLACEHOLDERS_SUPPORTED = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
if (PLACEHOLDERS_SUPPORTED) log(ChatColor.WHITE + " * Enabling PlaceholderAPI support...");
metadataFactory = setupMetadataFactory();
PacketFactory packetFactory = setupPacketFactory();
PLUGIN_FOLDER.mkdirs();
PATH_FOLDER.mkdirs();
getDataFolder().mkdirs();
log(ChatColor.WHITE + " * Loading configurations...");
Configs.init(PLUGIN_FOLDER);
ConfigManager configManager = new ConfigManager(getDataFolder());
log(ChatColor.WHITE + " * Defining NPC types...");
NpcTypeImpl.defineTypes();
log(ChatColor.WHITE + " * Registering components...");
log(ChatColor.WHITE + " * Starting tasks & registering components...");
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
new Metrics(this, PLUGIN_ID);
SCHEDULER = FoliaUtil.isFolia() ? new FoliaScheduler(this) : new SpigotScheduler(this);
BUNGEE_UTIL = new BungeeUtil(this);
Bukkit.getOnlinePlayers().forEach(User::get);
scheduler = FoliaUtil.isFolia() ? new FoliaScheduler(this) : new SpigotScheduler(this);
BungeeUtil bungeeUtil = new BungeeUtil(this);
userManager = new UserManager();
Bukkit.getOnlinePlayers().forEach(userManager::get);
pluginManager.registerEvents(new UserListener(userManager), this);
scheduler.runDelayedTimerAsync(new NpcVisibilityTask(npcRegistry, configManager), 60L, 10L);
skinCache = new SkinCache(configManager);
scheduler.runDelayedTimerAsync(new SkinCacheCleanTask(skinCache), 1200, 1200);
registerCommands();
log(ChatColor.WHITE + " * Starting tasks...");
new NpcVisibilityTask();
new SkinCacheCleanTask();
new UserListener(this);
if (Configs.config().checkForUpdates()) new UpdateNotificationListener(this, new UpdateChecker(this));
if (configManager.getConfig().checkForUpdates()) {
UpdateChecker updateChecker = new UpdateChecker(this.getDescription());
scheduler.runDelayedTimerAsync(updateChecker, 5L, 6000L);
pluginManager.registerEvents(new UpdateNotificationListener(this, adventure, updateChecker), this);
}
log(ChatColor.WHITE+ " * Loading NPCs...");
NpcRegistryImpl.get().reload();
ActionRegistry actionRegistry = new ActionRegistry(scheduler, adventure, bungeeUtil);
npcRegistry = new NpcRegistryImpl(configManager, this, packetFactory, actionRegistry);
npcRegistry.reload();
ZApiProvider.register(new ZNpcsApi());
ZApiProvider.register(this);
enabled = true;
log(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
log("");
if (Configs.config().debugEnabled()) {
if (configManager.getConfig().debugEnabled()) {
World world = Bukkit.getWorld("world");
if (world == null) world = Bukkit.getWorlds().get(0);
int i = 0;
for (NpcTypeImpl type : NpcTypeImpl.values()) {
NpcEntryImpl entry = NpcRegistryImpl.get().create(ZNpcsPlus.DEBUG_NPC_PREFIX + i, world, type, new ZLocation(i * 3, 200, 0, 0, 0));
NpcEntryImpl entry = npcRegistry.create("debug_npc_" + i, world, type, new ZLocation(i * 3, 200, 0, 0, 0));
entry.setProcessed(true);
NpcImpl npc = entry.getNpc();
npc.getHologram().addLine(Component.text("Hello, World!"));
@ -168,22 +170,61 @@ public class ZNpcsPlus extends JavaPlugin {
}
}
private PacketFactory setupPacketFactory() {
HashMap<ServerVersion, LazyLoader<? extends PacketFactory>> versions = new HashMap<>();
versions.put(ServerVersion.V_1_8, LazyLoader.of(() -> new V1_8PacketFactory(scheduler, metadataFactory)));
versions.put(ServerVersion.V_1_9, LazyLoader.of(() -> new V1_9PacketFactory(scheduler, metadataFactory)));
versions.put(ServerVersion.V_1_10, LazyLoader.of(() -> new V1_10PacketFactory(scheduler, metadataFactory)));
versions.put(ServerVersion.V_1_14, LazyLoader.of(() -> new V1_14PacketFactory(scheduler, metadataFactory)));
versions.put(ServerVersion.V_1_19, LazyLoader.of(() -> new V1_19PacketFactory(scheduler, metadataFactory)));
ServerVersion version = packetEvents.getServerManager().getVersion();
if (versions.containsKey(version)) return versions.get(version).get();
for (ServerVersion v : ServerVersion.reversedValues()) {
if (v.isNewerThan(version)) continue;
if (!versions.containsKey(v)) continue;
return versions.get(v).get();
}
throw new RuntimeException("Unsupported version!");
}
private MetadataFactory setupMetadataFactory() {
HashMap<ServerVersion, LazyLoader<? extends MetadataFactory>> versions = new HashMap<>();
versions.put(ServerVersion.V_1_8, LazyLoader.of(V1_8MetadataFactory::new));
versions.put(ServerVersion.V_1_9, LazyLoader.of(V1_9MetadataFactory::new));
versions.put(ServerVersion.V_1_10, LazyLoader.of(V1_10MetadataFactory::new));
versions.put(ServerVersion.V_1_13, LazyLoader.of(V1_13MetadataFactory::new));
versions.put(ServerVersion.V_1_14, LazyLoader.of(V1_14MetadataFactory::new));
versions.put(ServerVersion.V_1_16, LazyLoader.of(V1_16MetadataFactory::new));
versions.put(ServerVersion.V_1_17, LazyLoader.of(V1_17MetadataFactory::new));
ServerVersion version = packetEvents.getServerManager().getVersion();
if (versions.containsKey(version)) return versions.get(version).get();
for (ServerVersion v : ServerVersion.reversedValues()) {
if (v.isNewerThan(version)) continue;
if (!versions.containsKey(v)) continue;
return versions.get(v).get();
}
throw new RuntimeException("Unsupported version!");
}
@Override
public void onDisable() {
if (!enabled) return;
NpcRegistryImpl.get().save();
npcRegistry.save();
ZApiProvider.unregister();
Bukkit.getOnlinePlayers().forEach(User::remove);
ADVENTURE.close();
ADVENTURE = null;
Bukkit.getOnlinePlayers().forEach(userManager::remove);
adventure.close();
adventure = null;
}
private void registerCommands() {
// TODO: Messages in here
CommandManager manager = new CommandManager(this, ADVENTURE, context -> {});
CommandManager manager = new CommandManager(this, adventure, context -> {});
manager.registerParser(NpcTypeImpl.class, new NpcTypeParser(context -> {}));
manager.registerParser(NpcEntryImpl.class, new NpcEntryParser(context -> {}));
manager.registerParser(NpcEntryImpl.class, new NpcEntryParser(npcRegistry, context -> {}));
manager.registerParser(EntityPropertyImpl.class, new EntityPropertyParser(context -> {}));
manager.registerParser(Integer.class, new IntegerParser(context -> {}));
manager.registerParser(Boolean.class, new BooleanParser(context -> {}));
@ -191,25 +232,30 @@ public class ZNpcsPlus extends JavaPlugin {
manager.registerCommand("npc", new MultiCommand()
.addSubcommand("action", new ActionCommand())
.addSubcommand("create", new CreateCommand())
.addSubcommand("skin", new SkinCommand())
.addSubcommand("delete", new DeleteCommand())
.addSubcommand("move", new MoveCommand())
.addSubcommand("properties", new PropertiesCommand())
.addSubcommand("teleport", new TeleportCommand())
.addSubcommand("list", new ListCommand())
.addSubcommand("near", new NearCommand())
.addSubcommand("type", new TypeCommand())
.addSubcommand("create", new CreateCommand(npcRegistry))
.addSubcommand("skin", new SkinCommand(skinCache, npcRegistry))
.addSubcommand("delete", new DeleteCommand(npcRegistry, adventure))
.addSubcommand("move", new MoveCommand(npcRegistry))
.addSubcommand("properties", new PropertiesCommand(npcRegistry))
.addSubcommand("teleport", new TeleportCommand(npcRegistry))
.addSubcommand("list", new ListCommand(npcRegistry))
.addSubcommand("near", new NearCommand(npcRegistry))
.addSubcommand("type", new TypeCommand(npcRegistry))
.addSubcommand("storage", new MultiCommand()
.addSubcommand("save", new SaveAllCommand())
.addSubcommand("load", new LoadAllCommand()))
.addSubcommand("save", new SaveAllCommand(npcRegistry))
.addSubcommand("reload", new LoadAllCommand(npcRegistry)))
.addSubcommand("holo", new MultiCommand()
.addSubcommand("add", new HoloAddCommand())
.addSubcommand("delete", new HoloDeleteCommand())
.addSubcommand("info", new HoloInfoCommand())
.addSubcommand("insert", new HoloInsertCommand())
.addSubcommand("set", new HoloSetCommand())
.addSubcommand("add", new HoloAddCommand(npcRegistry, textSerializer))
.addSubcommand("delete", new HoloDeleteCommand(npcRegistry))
.addSubcommand("info", new HoloInfoCommand(npcRegistry))
.addSubcommand("insert", new HoloInsertCommand(npcRegistry, textSerializer))
.addSubcommand("set", new HoloSetCommand(npcRegistry, textSerializer))
)
);
}
@Override
public NpcRegistry getNpcRegistry() {
return npcRegistry;
}
}

View file

@ -15,16 +15,22 @@ import java.util.Collections;
import java.util.List;
public class CreateCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public CreateCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " create <id> <type>");
Player player = context.ensureSenderIsPlayer();
String id = context.popString();
if (NpcRegistryImpl.get().get(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED));
if (npcRegistry.get(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED));
NpcTypeImpl type = context.parse(NpcTypeImpl.class);
NpcEntryImpl entry = NpcRegistryImpl.get().create(id, player.getWorld(), type, new ZLocation(player.getLocation()));
NpcEntryImpl entry = npcRegistry.create(id, player.getWorld(), type, new ZLocation(player.getLocation()));
entry.enableEverything();
context.send(Component.text("Created a " + type.getName() + " NPC with ID " + id + ".", NamedTextColor.GREEN));
@ -32,7 +38,7 @@ public class CreateCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() == 2) return context.suggestStream(NpcTypeImpl.values().stream().map(NpcTypeImpl::getName));
return Collections.emptyList();
}

View file

@ -3,9 +3,9 @@ package lol.pyr.znpcsplus.commands;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -13,17 +13,25 @@ import java.util.Collections;
import java.util.List;
public class DeleteCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
private final BukkitAudiences adventure;
public DeleteCommand(NpcRegistryImpl npcRegistry, BukkitAudiences adventure) {
this.npcRegistry = npcRegistry;
this.adventure = adventure;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " delete <id>");
NpcEntryImpl entry = context.parse(NpcEntryImpl.class);
NpcRegistryImpl.get().delete(entry.getId());
ZNpcsPlus.ADVENTURE.sender(context.getSender()).sendMessage(Component.text("Deleted NPC with ID: " + entry.getId(), NamedTextColor.GREEN));
npcRegistry.delete(entry.getId());
adventure.sender(context.getSender()).sendMessage(Component.text("Deleted NPC with ID: " + entry.getId(), NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
return Collections.emptyList();
}
}

View file

@ -12,11 +12,17 @@ import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
public class ListCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public ListCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
TextComponent.Builder component = Component.text("Npc's:\n").color(NamedTextColor.GOLD).toBuilder();
for (String id : NpcRegistryImpl.get().modifiableIds()) {
NpcImpl npc = NpcRegistryImpl.get().get(id).getNpc();
for (String id : npcRegistry.modifiableIds()) {
NpcImpl npc = npcRegistry.get(id).getNpc();
ZLocation location = npc.getLocation();
component.append(Component.text("ID: " + id, NamedTextColor.GREEN))
.append(Component.text(" | ", NamedTextColor.GRAY))

View file

@ -15,6 +15,12 @@ import java.util.Collections;
import java.util.List;
public class MoveCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public MoveCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " move <id>");
@ -26,7 +32,7 @@ public class MoveCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
return Collections.emptyList();
}
}

View file

@ -12,13 +12,19 @@ import org.bukkit.entity.Player;
import java.util.stream.Collectors;
public class NearCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public NearCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
Player player = context.ensureSenderIsPlayer();
int raw = context.parse(Integer.class);
double radius = Math.pow(raw, 2);
String npcs = NpcRegistryImpl.get().allModifiable().stream()
String npcs = npcRegistry.allModifiable().stream()
.filter(entry -> entry.getNpc().getBukkitLocation().distanceSquared(player.getLocation()) < radius)
.map(NpcEntryImpl::getId)
.collect(Collectors.joining(", "));

View file

@ -14,6 +14,12 @@ import java.util.Collections;
import java.util.List;
public class PropertiesCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public PropertiesCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
NpcEntryImpl entry = context.parse(NpcEntryImpl.class);
@ -29,7 +35,7 @@ public class PropertiesCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() == 2) return context.suggestStream(context.suggestionParse(0, NpcEntryImpl.class)
.getNpc().getType().getAllowedProperties().stream().map(EntityPropertyImpl::getName));
if (context.argSize() == 3) {

View file

@ -8,6 +8,7 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcTypeImpl;
import lol.pyr.znpcsplus.skin.cache.SkinCache;
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
@ -18,6 +19,14 @@ import java.util.Collections;
import java.util.List;
public class SkinCommand implements CommandHandler {
private final SkinCache skinCache;
private final NpcRegistryImpl npcRegistry;
public SkinCommand(SkinCache skinCache, NpcRegistryImpl npcRegistry) {
this.skinCache = skinCache;
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " skin <id> <type> [value]");
@ -26,7 +35,7 @@ public class SkinCommand implements CommandHandler {
String type = context.popString();
if (type.equalsIgnoreCase("mirror")) {
npc.setProperty(EntityPropertyImpl.SKIN, new MirrorDescriptor());
npc.setProperty(EntityPropertyImpl.SKIN, new MirrorDescriptor(skinCache));
npc.respawn();
context.halt(Component.text("The NPC's skin will now mirror the player that it's being displayed to", NamedTextColor.GREEN));
}
@ -35,7 +44,7 @@ public class SkinCommand implements CommandHandler {
context.ensureArgsNotEmpty();
String name = context.dumpAllArgs();
context.send(Component.text("Fetching skin \"" + name + "\"...", NamedTextColor.GREEN));
PrefetchedDescriptor.forPlayer(name).thenAccept(skin -> {
PrefetchedDescriptor.forPlayer(skinCache, name).thenAccept(skin -> {
if (skin == null) {
context.send(Component.text("Failed to fetch skin, are you sure the player name is valid?", NamedTextColor.RED));
return;
@ -50,7 +59,7 @@ public class SkinCommand implements CommandHandler {
if (type.equalsIgnoreCase("dynamic")) {
context.ensureArgsNotEmpty();
String name = context.dumpAllArgs();
npc.setProperty(EntityPropertyImpl.SKIN, new FetchingDescriptor(name));
npc.setProperty(EntityPropertyImpl.SKIN, new FetchingDescriptor(skinCache, name));
npc.respawn();
context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\""));
}
@ -59,7 +68,7 @@ public class SkinCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic");
if (context.matchSuggestion("*", "static")) return context.suggestPlayers();
return Collections.emptyList();

View file

@ -15,6 +15,12 @@ import java.util.Collections;
import java.util.List;
public class TeleportCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public TeleportCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " teleport <id>");
@ -26,7 +32,7 @@ public class TeleportCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
return Collections.emptyList();
}
}

View file

@ -14,6 +14,12 @@ import java.util.Collections;
import java.util.List;
public class TypeCommand implements CommandHandler {
private final NpcRegistryImpl registry;
public TypeCommand(NpcRegistryImpl registry) {
this.registry = registry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " type <id> <type>");
@ -25,7 +31,7 @@ public class TypeCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(registry.modifiableIds());
if (context.argSize() == 2) return context.suggestStream(NpcTypeImpl.values().stream().map(NpcTypeImpl::getName));
return Collections.emptyList();
}

View file

@ -3,29 +3,37 @@ package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
public class HoloAddCommand implements CommandHandler {
private final NpcRegistryImpl registry;
private final LegacyComponentSerializer textSerializer;
public HoloAddCommand(NpcRegistryImpl registry, LegacyComponentSerializer textSerializer) {
this.registry = registry;
this.textSerializer = textSerializer;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo add <id> <text>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
context.ensureArgsNotEmpty();
hologram.addLine(ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs()));
hologram.addLine(textSerializer.deserialize(context.dumpAllArgs()));
context.send(Component.text("NPC line added!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(registry.modifiableIds());
return Collections.emptyList();
}
}

View file

@ -14,6 +14,12 @@ import java.util.List;
import java.util.stream.Stream;
public class HoloDeleteCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloDeleteCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo delete <id> <line>");
@ -26,7 +32,7 @@ public class HoloDeleteCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram().getLines().size())
.map(String::valueOf));

View file

@ -14,6 +14,12 @@ import java.util.Collections;
import java.util.List;
public class HoloInfoCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloInfoCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo info <id>");
@ -26,7 +32,7 @@ public class HoloInfoCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
return Collections.emptyList();
}
}

View file

@ -3,18 +3,26 @@ package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloInsertCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloInsertCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) {
this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo insert <id> <line> <text>");
@ -22,13 +30,13 @@ public class HoloInsertCommand implements CommandHandler {
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty();
hologram.insertLine(line, ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs()));
hologram.insertLine(line, componentSerializer.deserialize(context.dumpAllArgs()));
context.send(Component.text("NPC line inserted!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram().getLines().size())
.map(String::valueOf));

View file

@ -3,18 +3,26 @@ package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloSetCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloSetCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) {
this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo set <id> <line> <text>");
@ -23,18 +31,18 @@ public class HoloSetCommand implements CommandHandler {
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty();
hologram.removeLine(line);
hologram.insertLine(line, ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs()));
hologram.insertLine(line, componentSerializer.deserialize(context.dumpAllArgs()));
context.send(Component.text("NPC line set!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds());
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.modifiableIds());
if (context.argSize() >= 2) {
HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram();
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(hologram.getLines().size()).map(String::valueOf));
if (context.argSize() == 3) return context.suggestLiteral(ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.serialize(
if (context.argSize() == 3) return context.suggestLiteral(componentSerializer.serialize(
hologram.getLine(context.suggestionParse(1, Integer.class))));
}
return Collections.emptyList();

View file

@ -10,13 +10,16 @@ import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import java.util.Deque;
public class NpcEntryParser extends ParserType<NpcEntryImpl> {
public NpcEntryParser(Message<CommandContext> message) {
private final NpcRegistryImpl npcRegistry;
public NpcEntryParser(NpcRegistryImpl npcRegistry, Message<CommandContext> message) {
super(message);
this.npcRegistry = npcRegistry;
}
@Override
public NpcEntryImpl parse(Deque<String> deque) throws CommandExecutionException {
NpcEntryImpl entry = NpcRegistryImpl.get().get(deque.pop());
NpcEntryImpl entry = npcRegistry.get(deque.pop());
if (entry == null || !entry.isAllowCommandModification()) throw new CommandExecutionException();
return entry;
}

View file

@ -12,10 +12,16 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
public class LoadAllCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public LoadAllCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
CompletableFuture.runAsync(() -> {
NpcRegistryImpl.get().reload();
npcRegistry.reload();
context.send(Component.text("All NPCs have been re-loaded from storage", NamedTextColor.GREEN));
});
}

View file

@ -12,10 +12,16 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
public class SaveAllCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public SaveAllCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
CompletableFuture.runAsync(() -> {
NpcRegistryImpl.get().save();
npcRegistry.save();
context.send(Component.text("All NPCs have been saved to storage", NamedTextColor.GREEN));
});
}

View file

@ -1,6 +1,5 @@
package lol.pyr.znpcsplus.config;
import lol.pyr.znpcsplus.ZNpcsPlus;
import space.arim.dazzleconf.ConfigurationFactory;
import space.arim.dazzleconf.ConfigurationOptions;
import space.arim.dazzleconf.error.ConfigFormatSyntaxException;
@ -13,13 +12,22 @@ import space.arim.dazzleconf.serialiser.ValueSerialiser;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
public class Configs {
private volatile static MainConfig config;
private static ConfigurationHelper<MainConfig> configHelper;
public class ConfigManager {
private final static Logger logger = Logger.getLogger("ZNPCsPlus Configuration Manager");
private volatile static MessageConfig messages;
private static ConfigurationHelper<MessageConfig> messagesHelper;
private volatile MainConfig config;
private final ConfigurationHelper<MainConfig> configHelper;
private volatile MessageConfig messages;
private final ConfigurationHelper<MessageConfig> messagesHelper;
public ConfigManager(File pluginFolder) {
configHelper = createHelper(MainConfig.class, new File(pluginFolder, "config.yaml"));
messagesHelper = createHelper(MessageConfig.class, new File(pluginFolder, "messages.yaml"), new ComponentSerializer());
reload();
}
private static <T> ConfigurationHelper<T> createHelper(Class<T> configClass, File file, ValueSerialiser<?>... serialisers) {
SnakeYamlOptions yamlOptions = new SnakeYamlOptions.Builder().commentMode(CommentMode.fullComments()).build();
@ -29,33 +37,27 @@ public class Configs {
return new ConfigurationHelper<>(file.getParentFile().toPath(), file.getName(), configFactory);
}
public static void init(File pluginFolder) {
configHelper = createHelper(MainConfig.class, new File(pluginFolder, "config.yaml"));
messagesHelper = createHelper(MessageConfig.class, new File(pluginFolder, "messages.yaml"), new ComponentSerializer());
load();
}
public static void load() {
public void reload() {
try {
config = configHelper.reloadConfigData();
messages = messagesHelper.reloadConfigData();
} catch (IOException e) {
ZNpcsPlus.LOGGER.severe("Couldn't open config file!");
logger.severe("Couldn't open config file!");
e.printStackTrace();
} catch (ConfigFormatSyntaxException e) {
ZNpcsPlus.LOGGER.severe("Invalid config syntax!");
logger.severe("Invalid config syntax!");
e.printStackTrace();
} catch (InvalidConfigException e) {
ZNpcsPlus.LOGGER.severe("Invalid config value!");
logger.severe("Invalid config value!");
e.printStackTrace();
}
}
public static MainConfig config() {
public MainConfig getConfig() {
return config;
}
public static MessageConfig messages() {
public MessageConfig getMessages() {
return messages;
}
}

View file

@ -89,7 +89,7 @@ public class EntityPropertyImpl<T> implements EntityProperty<T> {
private final static PropertyDeserializer<Component> COMPONENT_DESERIALIZER = str -> MiniMessage.miniMessage().deserialize(str);
private final static PropertySerializer<SkinDescriptor> DESCRIPTOR_SERIALIZER = descriptor -> ((BaseSkinDescriptor) descriptor).serialize();
private final static PropertyDeserializer<SkinDescriptor> DESCRIPTOR_DESERIALIZER = BaseSkinDescriptor::deserialize;
private final static PropertyDeserializer<SkinDescriptor> DESCRIPTOR_DESERIALIZER = property -> null; // TODO: An actual property registry // BaseSkinDescriptor::deserialize;
public static EntityPropertyImpl<NamedTextColor> GLOW = new EntityPropertyImpl<>("glow", NamedTextColor.class, COLOR_SERIALIZER, COLOR_DESERIALIZER);
public static EntityPropertyImpl<Boolean> SKIN_LAYERS = new EntityPropertyImpl<>("skin_layers", true, BOOLEAN_SERIALIZER, BOOLEAN_DESERIALIZER);

View file

@ -13,6 +13,8 @@ import java.util.Collection;
import java.util.UUID;
public class PacketEntity {
private final PacketFactory packetFactory;
private final PropertyHolder properties;
private final int entityId;
private final UUID uuid;
@ -20,7 +22,8 @@ public class PacketEntity {
private final EntityType type;
private ZLocation location;
public PacketEntity(PropertyHolder properties, EntityType type, ZLocation location) {
public PacketEntity(PacketFactory packetFactory, PropertyHolder properties, EntityType type, ZLocation location) {
this.packetFactory = packetFactory;
this.properties = properties;
this.entityId = reserveEntityID();
this.uuid = UUID.randomUUID();
@ -46,25 +49,25 @@ public class PacketEntity {
public void setLocation(ZLocation location, Collection<Player> viewers) {
this.location = location;
for (Player viewer : viewers) PacketFactory.get().teleportEntity(viewer, this);
for (Player viewer : viewers) packetFactory.teleportEntity(viewer, this);
}
public void spawn(Player player) {
if (type == EntityTypes.PLAYER) PacketFactory.get().spawnPlayer(player, this, properties);
else PacketFactory.get().spawnEntity(player, this, properties);
if (type == EntityTypes.PLAYER) packetFactory.spawnPlayer(player, this, properties);
else packetFactory.spawnEntity(player, this, properties);
}
public void despawn(Player player) {
PacketFactory.get().destroyEntity(player, this, properties);
packetFactory.destroyEntity(player, this, properties);
}
public void refreshMeta(Player player) {
PacketFactory.get().sendAllMetadata(player, this, properties);
packetFactory.sendAllMetadata(player, this, properties);
}
public void remakeTeam(Player player) {
PacketFactory.get().removeTeam(player, this);
PacketFactory.get().createTeam(player, this, properties);
packetFactory.removeTeam(player, this);
packetFactory.createTeam(player, this, properties);
}
private static int reserveEntityID() {

View file

@ -1,7 +1,8 @@
package lol.pyr.znpcsplus.hologram;
import lol.pyr.znpcsplus.api.hologram.Hologram;
import lol.pyr.znpcsplus.config.Configs;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.Viewable;
import lol.pyr.znpcsplus.util.ZLocation;
import net.kyori.adventure.text.Component;
@ -12,15 +13,20 @@ import java.util.Collections;
import java.util.List;
public class HologramImpl extends Viewable implements Hologram {
private final ConfigManager configManager;
private final PacketFactory packetFactory;
private ZLocation location;
private final List<HologramLine> lines = new ArrayList<>();
public HologramImpl(ZLocation location) {
public HologramImpl(ConfigManager configManager, PacketFactory packetFactory, ZLocation location) {
this.configManager = configManager;
this.packetFactory = packetFactory;
this.location = location;
}
public void addLine(Component line) {
HologramLine newLine = new HologramLine(null, line);
HologramLine newLine = new HologramLine(packetFactory, null, line);
lines.add(newLine);
relocateLines();
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
@ -46,7 +52,7 @@ public class HologramImpl extends Viewable implements Hologram {
}
public void insertLine(int index, Component line) {
HologramLine newLine = new HologramLine(null, line);
HologramLine newLine = new HologramLine(packetFactory, null, line);
lines.add(index, newLine);
relocateLines();
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
@ -72,7 +78,7 @@ public class HologramImpl extends Viewable implements Hologram {
}
private void relocateLines(HologramLine newLine) {
final double lineSpacing = Configs.config().lineSpacing();
final double lineSpacing = configManager.getConfig().lineSpacing();
double height = location.getY() + (lines.size() - 1) * lineSpacing;
for (HologramLine line : lines) {
line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers());

View file

@ -5,6 +5,7 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.ZLocation;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
@ -15,9 +16,9 @@ public class HologramLine implements PropertyHolder {
private Component text;
private final PacketEntity armorStand;
public HologramLine(ZLocation location, Component text) {
public HologramLine(PacketFactory packetFactory, ZLocation location, Component text) {
this.text = text;
armorStand = new PacketEntity(this, EntityTypes.ARMOR_STAND, location);
armorStand = new PacketEntity(packetFactory, this, EntityTypes.ARMOR_STAND, location);
}
public Component getText() {

View file

@ -0,0 +1,46 @@
package lol.pyr.znpcsplus.interaction;
import lol.pyr.znpcsplus.interaction.types.*;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.BungeeUtil;
import lol.pyr.znpcsplus.util.StringSerializer;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ActionRegistry {
private final Map<Class<?>, StringSerializer<?>> serializerMap = new HashMap<>();
public ActionRegistry(TaskScheduler taskScheduler, BukkitAudiences adventure, BungeeUtil bungeeUtil) {
register(ConsoleCommandAction.class, new ConsoleCommandActionSerializer(taskScheduler));
register(PlayerCommandAction.class, new PlayerCommandActionSerializer(taskScheduler));
register(SwitchServerAction.class, new SwitchServerActionSerializer(bungeeUtil));
register(MessageAction.class, new MessageActionSerializer(adventure));
}
public <T extends InteractionAction> void register(Class<T> clazz, StringSerializer<T> serializer) {
serializerMap.put(clazz, serializer);
}
@SuppressWarnings("unchecked")
public <T extends InteractionAction> T deserialize(String str) {
try {
String[] split = str.split(";");
Class<?> clazz = Class.forName(split[0]);
StringSerializer<T> serializer = (StringSerializer<T>) serializerMap.get(clazz);
if (serializer == null) return null;
return serializer.deserialize(String.join(";", Arrays.copyOfRange(split, 1, split.length)));
} catch (ClassNotFoundException e) {
return null;
}
}
@SuppressWarnings("unchecked")
public <T extends InteractionAction> String serialize(T action) {
StringSerializer<T> serializer = (StringSerializer<T>) serializerMap.get(action.getClass());
if (serializer == null) return null;
return action.getClass().getName() + ";" + serializer.serialize(action);
}
}

View file

@ -4,15 +4,13 @@ import org.bukkit.entity.Player;
import java.util.UUID;
public abstract class NpcAction {
public abstract class InteractionAction {
private final UUID id;
private final long delay;
protected final String argument;
protected NpcAction(long delay, String argument) {
protected InteractionAction(long delay) {
this.id = UUID.randomUUID();
this.delay = delay;
this.argument = argument;
}
public UUID getUuid() {
@ -23,11 +21,5 @@ public abstract class NpcAction {
return delay;
}
public String getArgument() {
return argument;
}
public abstract void run(Player player);
public abstract NpcActionType getType();
}

View file

@ -8,23 +8,32 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.user.User;
import lol.pyr.znpcsplus.user.UserManager;
import org.bukkit.entity.Player;
public class InteractionPacketListener implements PacketListener {
private final UserManager userManager;
private final NpcRegistryImpl npcRegistry;
public InteractionPacketListener(UserManager userManager, NpcRegistryImpl npcRegistry) {
this.userManager = userManager;
this.npcRegistry = npcRegistry;
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() != PacketType.Play.Client.INTERACT_ENTITY) return;
Player player = (Player) event.getPlayer();
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
User user = User.get(player);
User user = userManager.get(player);
if (!user.canInteract()) return;
NpcEntryImpl entry = NpcRegistryImpl.get().getByEntityId(packet.getEntityId());
NpcEntryImpl entry = npcRegistry.getByEntityId(packet.getEntityId());
if (entry == null || !entry.isProcessed()) return;
NpcImpl npc = entry.getNpc();
for (NpcAction action : npc.getActions()) {
for (InteractionAction action : npc.getActions()) {
if (action.getCooldown() > 0 && !user.actionCooldownCheck(action)) continue;
action.run(player);
}

View file

@ -1,6 +0,0 @@
package lol.pyr.znpcsplus.interaction;
@FunctionalInterface
interface NpcActionDeserializer {
NpcAction deserialize(long delay, String argument);
}

View file

@ -1,21 +0,0 @@
package lol.pyr.znpcsplus.interaction;
import lol.pyr.znpcsplus.interaction.types.*;
public enum NpcActionType implements NpcActionDeserializer {
CONSOLE_CMD(ConsoleCommandAction::new),
MESSAGE(MessageAction::new),
PLAYER_CMD(PlayerCommandAction::new),
SERVER(SwitchServerAction::new);
private final NpcActionDeserializer deserializer;
NpcActionType(NpcActionDeserializer deserializer) {
this.deserializer = deserializer;
}
@Override
public NpcAction deserialize(long delay, String str) {
return deserializer.deserialize(delay, str);
}
}

View file

@ -1,25 +1,28 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.NpcActionType;
import me.clip.placeholderapi.PlaceholderAPI;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.PapiUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class ConsoleCommandAction extends NpcAction {
public ConsoleCommandAction(long delay, String argument) {
super(delay, argument);
public class ConsoleCommandAction extends InteractionAction {
private final TaskScheduler scheduler;
private final String command;
public ConsoleCommandAction(TaskScheduler scheduler, String command, long delay) {
super(delay);
this.scheduler = scheduler;
this.command = command;
}
@Override
public void run(Player player) {
String cmd = argument.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString());
ZNpcsPlus.SCHEDULER.runSync(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), ZNpcsPlus.PLACEHOLDERS_SUPPORTED ? PlaceholderAPI.setPlaceholders(player, cmd) : cmd));
String cmd = command.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString());
scheduler.runSync(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), PapiUtil.set(player, cmd)));
}
@Override
public NpcActionType getType() {
return NpcActionType.CONSOLE_CMD;
public String getCommand() {
return command;
}
}

View file

@ -0,0 +1,26 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.StringSerializer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class ConsoleCommandActionSerializer implements StringSerializer<ConsoleCommandAction> {
private final TaskScheduler scheduler;
public ConsoleCommandActionSerializer(TaskScheduler scheduler) {
this.scheduler = scheduler;
}
@Override
public String serialize(ConsoleCommandAction obj) {
return Base64.getEncoder().encodeToString(obj.getCommand().getBytes(StandardCharsets.UTF_8)) + ";" + obj.getCooldown();
}
@Override
public ConsoleCommandAction deserialize(String str) {
String[] split = str.split(";");
return new ConsoleCommandAction(scheduler, new String(Base64.getDecoder().decode(split[0]), StandardCharsets.UTF_8), Long.parseLong(split[1]));
}
}

View file

@ -1,27 +1,26 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.NpcActionType;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.entity.Player;
public class MessageAction extends NpcAction {
public class MessageAction extends InteractionAction {
private final BukkitAudiences adventure;
private final Component message;
public MessageAction(long delay, String argument) {
super(delay, argument);
message = MiniMessage.miniMessage().deserialize(argument);
public MessageAction(BukkitAudiences adventure, Component message, long delay) {
super(delay);
this.adventure = adventure;
this.message = message;
}
@Override
public void run(Player player) {
ZNpcsPlus.ADVENTURE.player(player).sendMessage(message);
adventure.player(player).sendMessage(message);
}
@Override
public NpcActionType getType() {
return NpcActionType.MESSAGE;
public Component getMessage() {
return message;
}
}

View file

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.util.StringSerializer;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.minimessage.MiniMessage;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class MessageActionSerializer implements StringSerializer<MessageAction> {
private final BukkitAudiences adventure;
public MessageActionSerializer(BukkitAudiences adventure) {
this.adventure = adventure;
}
@Override
public String serialize(MessageAction obj) {
return Base64.getEncoder().encodeToString(MiniMessage.miniMessage().serialize(obj.getMessage()).getBytes(StandardCharsets.UTF_8)) + ";" + obj.getCooldown();
}
@Override
public MessageAction deserialize(String str) {
String[] split = str.split(";");
return new MessageAction(adventure, MiniMessage.miniMessage().deserialize(new String(Base64.getDecoder().decode(split[0]), StandardCharsets.UTF_8)), Long.parseLong(split[1]));
}
}

View file

@ -1,25 +1,28 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.NpcActionType;
import me.clip.placeholderapi.PlaceholderAPI;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.PapiUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class PlayerCommandAction extends NpcAction {
public PlayerCommandAction(long delay, String argument) {
super(delay, argument);
public class PlayerCommandAction extends InteractionAction {
private final TaskScheduler scheduler;
private final String command;
public PlayerCommandAction(TaskScheduler scheduler, String command, long delay) {
super(delay);
this.scheduler = scheduler;
this.command = command;
}
@Override
public void run(Player player) {
String cmd = argument.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString());
ZNpcsPlus.SCHEDULER.runSync(() -> Bukkit.dispatchCommand(player, ZNpcsPlus.PLACEHOLDERS_SUPPORTED ? PlaceholderAPI.setPlaceholders(player, cmd) : cmd));
String cmd = command.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString());
scheduler.runSync(() -> Bukkit.dispatchCommand(player, PapiUtil.set(player, cmd)));
}
@Override
public NpcActionType getType() {
return NpcActionType.PLAYER_CMD;
public String getCommand() {
return command;
}
}

View file

@ -0,0 +1,26 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.StringSerializer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class PlayerCommandActionSerializer implements StringSerializer<PlayerCommandAction> {
private final TaskScheduler scheduler;
public PlayerCommandActionSerializer(TaskScheduler scheduler) {
this.scheduler = scheduler;
}
@Override
public String serialize(PlayerCommandAction obj) {
return Base64.getEncoder().encodeToString(obj.getCommand().getBytes(StandardCharsets.UTF_8)) + ";" + obj.getCooldown();
}
@Override
public PlayerCommandAction deserialize(String str) {
String[] split = str.split(";");
return new PlayerCommandAction(scheduler, new String(Base64.getDecoder().decode(split[0]), StandardCharsets.UTF_8), Long.parseLong(split[1]));
}
}

View file

@ -1,22 +1,25 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.NpcActionType;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import lol.pyr.znpcsplus.util.BungeeUtil;
import org.bukkit.entity.Player;
public class SwitchServerAction extends NpcAction {
public SwitchServerAction(long delay, String argument) {
super(delay, argument);
public class SwitchServerAction extends InteractionAction {
private final BungeeUtil bungeeUtil;
private final String server;
public SwitchServerAction(BungeeUtil bungeeUtil, String server, long delay) {
super(delay);
this.bungeeUtil = bungeeUtil;
this.server = server;
}
@Override
public void run(Player player) {
ZNpcsPlus.BUNGEE_UTIL.sendPlayerToServer(player, argument);
bungeeUtil.sendPlayerToServer(player, server);
}
@Override
public NpcActionType getType() {
return NpcActionType.SERVER;
public String getServer() {
return server;
}
}

View file

@ -0,0 +1,26 @@
package lol.pyr.znpcsplus.interaction.types;
import lol.pyr.znpcsplus.util.BungeeUtil;
import lol.pyr.znpcsplus.util.StringSerializer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class SwitchServerActionSerializer implements StringSerializer<SwitchServerAction> {
private final BungeeUtil bungeeUtil;
public SwitchServerActionSerializer(BungeeUtil bungeeUtil) {
this.bungeeUtil = bungeeUtil;
}
@Override
public String serialize(SwitchServerAction obj) {
return Base64.getEncoder().encodeToString(obj.getServer().getBytes(StandardCharsets.UTF_8)) + ";" + obj.getCooldown();
}
@Override
public SwitchServerAction deserialize(String str) {
String[] split = str.split(";");
return new SwitchServerAction(bungeeUtil, new String(Base64.getDecoder().decode(split[0]), StandardCharsets.UTF_8), Long.parseLong(split[1]));
}
}

View file

@ -1,15 +1,9 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.util.LazyLoader;
import net.kyori.adventure.text.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* 1.8 https://wiki.vg/index.php?title=Entity_metadata&oldid=7415
@ -31,33 +25,4 @@ public interface MetadataFactory {
EntityData silent(boolean enabled);
Collection<EntityData> name(Component name);
EntityData noGravity();
MetadataFactory factory = get();
static MetadataFactory get() {
if (factory != null) return factory;
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
Map<ServerVersion, LazyLoader<? extends MetadataFactory>> factories = buildFactoryMap();
if (factories.containsKey(version)) return factories.get(version).get();
for (ServerVersion v : ServerVersion.reversedValues()) {
if (v.isNewerThan(version)) continue;
if (!factories.containsKey(v)) continue;
MetadataFactory f = factories.get(v).get();
ZNpcsPlus.debug("Using MetadataFactory Version " + v.name() + " (" + f.getClass().getName() + ")");
return f;
}
throw new RuntimeException("Unsupported version!");
}
static Map<ServerVersion, LazyLoader<? extends MetadataFactory>> buildFactoryMap() {
HashMap<ServerVersion, LazyLoader<? extends MetadataFactory>> map = new HashMap<>();
map.put(ServerVersion.V_1_8, LazyLoader.of(V1_8Factory::new));
map.put(ServerVersion.V_1_9, LazyLoader.of(V1_9Factory::new));
map.put(ServerVersion.V_1_10, LazyLoader.of(V1_10Factory::new));
map.put(ServerVersion.V_1_13, LazyLoader.of(V1_13Factory::new));
map.put(ServerVersion.V_1_14, LazyLoader.of(V1_14Factory::new));
map.put(ServerVersion.V_1_16, LazyLoader.of(V1_16Factory::new));
map.put(ServerVersion.V_1_17, LazyLoader.of(V1_17Factory::new));
return map;
}
}

View file

@ -3,7 +3,7 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
public class V1_10Factory extends V1_9Factory {
public class V1_10MetadataFactory extends V1_9MetadataFactory {
@Override
public EntityData noGravity() {
return new EntityData(5, EntityDataTypes.BOOLEAN, true);

View file

@ -9,7 +9,7 @@ import net.kyori.adventure.text.Component;
import java.util.Collection;
import java.util.Optional;
public class V1_13Factory extends V1_10Factory {
public class V1_13MetadataFactory extends V1_10MetadataFactory {
@Override
public Collection<EntityData> name(Component name) {
return ListUtil.immutableList(

View file

@ -2,7 +2,7 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
public class V1_14Factory extends V1_13Factory {
public class V1_14MetadataFactory extends V1_13MetadataFactory {
@Override
public EntityData skinLayers(boolean enabled) {
return createSkinLayers(15, enabled);

View file

@ -2,7 +2,7 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
public class V1_16Factory extends V1_14Factory {
public class V1_16MetadataFactory extends V1_14MetadataFactory {
@Override
public EntityData skinLayers(boolean enabled) {
return createSkinLayers(16, enabled);

View file

@ -2,7 +2,7 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
public class V1_17Factory extends V1_16Factory {
public class V1_17MetadataFactory extends V1_16MetadataFactory {
@Override
public EntityData skinLayers(boolean enabled) {
return createSkinLayers(17, enabled);

View file

@ -8,7 +8,7 @@ import net.kyori.adventure.text.Component;
import java.util.Collection;
public class V1_8Factory implements MetadataFactory {
public class V1_8MetadataFactory implements MetadataFactory {
@Override
public EntityData skinLayers(boolean enabled) {
return createSkinLayers(12, enabled);

View file

@ -8,7 +8,7 @@ import net.kyori.adventure.text.Component;
import java.util.Collection;
public class V1_9Factory extends V1_8Factory {
public class V1_9MetadataFactory extends V1_8MetadataFactory {
@Override
public EntityData skinLayers(boolean enabled) {
return createSkinLayers(13, enabled);

View file

@ -2,10 +2,12 @@ package lol.pyr.znpcsplus.npc;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.npc.Npc;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.Viewable;
import lol.pyr.znpcsplus.util.ZLocation;
import org.bukkit.Bukkit;
@ -16,6 +18,7 @@ import org.bukkit.entity.Player;
import java.util.*;
public class NpcImpl extends Viewable implements Npc {
private final PacketFactory packetFactory;
private final String worldName;
private PacketEntity entity;
private ZLocation location;
@ -23,25 +26,26 @@ public class NpcImpl extends Viewable implements Npc {
private final HologramImpl hologram;
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
private final Set<NpcAction> actions = new HashSet<>();
private final Set<InteractionAction> actions = new HashSet<>();
protected NpcImpl(World world, NpcTypeImpl type, ZLocation location) {
this(world.getName(), type, location);
protected NpcImpl(ConfigManager configManager, World world, NpcTypeImpl type, ZLocation location, PacketFactory packetFactory) {
this(configManager, packetFactory, world.getName(), type, location);
}
public NpcImpl(String world, NpcTypeImpl type, ZLocation location) {
public NpcImpl(ConfigManager configManager, PacketFactory packetFactory, String world, NpcTypeImpl type, ZLocation location) {
this.packetFactory = packetFactory;
this.worldName = world;
this.type = type;
this.location = location;
entity = new PacketEntity(this, type.getType(), location);
hologram = new HologramImpl(location.withY(location.getY() + type.getHologramOffset()));
entity = new PacketEntity(packetFactory, this, type.getType(), location);
hologram = new HologramImpl(configManager, packetFactory, location.withY(location.getY() + type.getHologramOffset()));
}
public void setType(NpcTypeImpl type) {
UNSAFE_hideAll();
this.type = type;
entity = new PacketEntity(this, type.getType(), entity.getLocation());
entity = new PacketEntity(packetFactory, this, type.getType(), entity.getLocation());
UNSAFE_showAll();
}
@ -130,11 +134,11 @@ public class NpcImpl extends Viewable implements Npc {
return Collections.unmodifiableSet(propertyMap.keySet());
}
public Collection<NpcAction> getActions() {
public Collection<InteractionAction> getActions() {
return Collections.unmodifiableSet(actions);
}
public void addAction(NpcAction action) {
public void addAction(InteractionAction action) {
actions.add(action);
}
}

View file

@ -1,8 +1,11 @@
package lol.pyr.znpcsplus.npc;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
import lol.pyr.znpcsplus.api.npc.NpcType;
import lol.pyr.znpcsplus.config.Configs;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.storage.NpcStorage;
import lol.pyr.znpcsplus.util.ZLocation;
import org.bukkit.World;
@ -14,25 +17,23 @@ import java.util.Map;
import java.util.stream.Collectors;
public class NpcRegistryImpl implements NpcRegistry {
private final static NpcRegistryImpl registry = new NpcRegistryImpl();
public static NpcRegistryImpl get() {
return registry;
}
private final NpcStorage storage;
private final PacketFactory packetFactory;
private final ConfigManager configManager;
private final NpcStorage STORAGE;
private NpcRegistryImpl() {
if (registry != null) throw new UnsupportedOperationException("This class can only be instanciated once!");
STORAGE = Configs.config().storageType().create();
public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry) {
storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry);
this.packetFactory = packetFactory;
this.configManager = configManager;
}
public void reload() {
npcMap.clear();
for (NpcEntryImpl entry : STORAGE.loadNpcs()) npcMap.put(entry.getId(), entry);
for (NpcEntryImpl entry : storage.loadNpcs()) npcMap.put(entry.getId(), entry);
}
public void save() {
STORAGE.saveNpcs(npcMap.values().stream().filter(NpcEntryImpl::isSave).collect(Collectors.toList()));
storage.saveNpcs(npcMap.values().stream().filter(NpcEntryImpl::isSave).collect(Collectors.toList()));
}
private final Map<String, NpcEntryImpl> npcMap = new HashMap<>();
@ -73,7 +74,7 @@ public class NpcRegistryImpl implements NpcRegistry {
public NpcEntryImpl create(String id, World world, NpcTypeImpl type, ZLocation location) {
id = id.toLowerCase();
if (npcMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!");
NpcImpl npc = new NpcImpl(world, type, location);
NpcImpl npc = new NpcImpl(configManager, world, type, location, packetFactory);
NpcEntryImpl entry = new NpcEntryImpl(id, npc);
npcMap.put(id, entry);
return entry;

View file

@ -1,15 +1,12 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.util.LazyLoader;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public interface PacketFactory {
@ -24,31 +21,4 @@ public interface PacketFactory {
Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
PacketFactory factory = get();
static PacketFactory get() {
if (factory != null) return factory;
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
Map<ServerVersion, LazyLoader<? extends PacketFactory>> factories = buildFactoryMap();
if (factories.containsKey(version)) return factories.get(version).get();
for (ServerVersion v : ServerVersion.reversedValues()) {
if (v.isNewerThan(version)) continue;
if (!factories.containsKey(v)) continue;
PacketFactory f = factories.get(v).get();
ZNpcsPlus.debug("Using PacketFactory Version " + v.name() + " (" + f.getClass().getName() + ")");
return f;
}
throw new RuntimeException("Unsupported version!");
}
static Map<ServerVersion, LazyLoader<? extends PacketFactory>> buildFactoryMap() {
HashMap<ServerVersion, LazyLoader<? extends PacketFactory>> map = new HashMap<>();
map.put(ServerVersion.V_1_8, LazyLoader.of(V1_8Factory::new));
map.put(ServerVersion.V_1_9, LazyLoader.of(V1_9Factory::new));
map.put(ServerVersion.V_1_10, LazyLoader.of(V1_10Factory::new));
map.put(ServerVersion.V_1_14, LazyLoader.of(V1_14Factory::new));
map.put(ServerVersion.V_1_19, LazyLoader.of(V1_19Factory::new));
return map;
}
}

View file

@ -4,15 +4,20 @@ import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import org.bukkit.entity.Player;
import java.util.Map;
public class V1_10Factory extends V1_9Factory {
public class V1_10PacketFactory extends V1_9PacketFactory {
public V1_10PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory) {
super(scheduler, metadataFactory);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, MetadataFactory.get().noGravity());
add(data, metadataFactory.noGravity());
return data;
}
}

View file

@ -4,12 +4,18 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.ZLocation;
import org.bukkit.entity.Player;
import java.util.Optional;
public class V1_14Factory extends V1_10Factory {
public class V1_14PacketFactory extends V1_10PacketFactory {
public V1_14PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory) {
super(scheduler, metadataFactory);
}
@Override
public void spawnEntity(Player player, PacketEntity entity, PropertyHolder properties) {
ZLocation location = entity.getLocation();

View file

@ -7,13 +7,19 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPl
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import java.util.EnumSet;
import java.util.concurrent.CompletableFuture;
public class V1_19Factory extends V1_14Factory {
public class V1_19PacketFactory extends V1_14PacketFactory {
public V1_19PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory) {
super(scheduler, metadataFactory);
}
@Override
public CompletableFuture<Void> addTabPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
if (entity.getType() != EntityTypes.PLAYER) return CompletableFuture.completedFuture(null);

View file

@ -10,11 +10,11 @@ import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.*;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
import lol.pyr.znpcsplus.util.ZLocation;
import net.kyori.adventure.text.Component;
@ -24,7 +24,15 @@ import org.bukkit.entity.Player;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class V1_8Factory implements PacketFactory {
public class V1_8PacketFactory implements PacketFactory {
protected final TaskScheduler scheduler;
protected final MetadataFactory metadataFactory;
public V1_8PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory) {
this.scheduler = scheduler;
this.metadataFactory = metadataFactory;
}
@Override
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
addTabPlayer(player, entity, properties).thenAccept(ignored -> {
@ -34,7 +42,7 @@ public class V1_8Factory implements PacketFactory {
entity.getUuid(), location.toVector3d(), location.getYaw(), location.getPitch(), Collections.emptyList()));
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
sendAllMetadata(player, entity, properties);
ZNpcsPlus.SCHEDULER.runLaterAsync(() -> removeTabPlayer(player, entity), 60);
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), 60);
});
}
@ -106,10 +114,10 @@ public class V1_8Factory implements PacketFactory {
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
HashMap<Integer, EntityData> data = new HashMap<>();
if (entity.getType() == EntityTypes.PLAYER) add(data, MetadataFactory.get().skinLayers(properties.getProperty(EntityPropertyImpl.SKIN_LAYERS)));
add(data, MetadataFactory.get().effects(properties.getProperty(EntityPropertyImpl.FIRE), false, properties.getProperty(EntityPropertyImpl.INVISIBLE)));
add(data, MetadataFactory.get().silent(properties.getProperty(EntityPropertyImpl.SILENT)));
if (properties.hasProperty(EntityPropertyImpl.NAME)) addAll(data, MetadataFactory.get().name(properties.getProperty(EntityPropertyImpl.NAME)));
if (entity.getType() == EntityTypes.PLAYER) add(data, metadataFactory.skinLayers(properties.getProperty(EntityPropertyImpl.SKIN_LAYERS)));
add(data, metadataFactory.effects(properties.getProperty(EntityPropertyImpl.FIRE), false, properties.getProperty(EntityPropertyImpl.INVISIBLE)));
add(data, metadataFactory.silent(properties.getProperty(EntityPropertyImpl.SILENT)));
if (properties.hasProperty(EntityPropertyImpl.NAME)) addAll(data, metadataFactory.name(properties.getProperty(EntityPropertyImpl.NAME)));
return data;
}

View file

@ -5,15 +5,20 @@ import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import org.bukkit.entity.Player;
import java.util.Map;
public class V1_9Factory extends V1_8Factory {
public class V1_9PacketFactory extends V1_8PacketFactory {
public V1_9PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory) {
super(scheduler, metadataFactory);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, MetadataFactory.get().effects(properties.getProperty(EntityPropertyImpl.FIRE), properties.hasProperty(EntityPropertyImpl.GLOW), properties.getProperty(EntityPropertyImpl.INVISIBLE)));
add(data, metadataFactory.effects(properties.getProperty(EntityPropertyImpl.FIRE), properties.hasProperty(EntityPropertyImpl.GLOW), properties.getProperty(EntityPropertyImpl.INVISIBLE)));
return data;
}
}

View file

@ -1,13 +1,14 @@
package lol.pyr.znpcsplus.reflection;
import lol.pyr.znpcsplus.util.VersionUtil;
import lol.pyr.znpcsplus.ZNpcsPlus;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.logging.Logger;
public abstract class ReflectionLazyLoader<T> {
private final static Logger logger = Logger.getLogger("ZNPCsPlus Reflection");
protected final List<String> possibleClassNames;
protected List<Class<?>> reflectionClasses = new ArrayList<>();
protected final boolean strict;
@ -34,25 +35,21 @@ public abstract class ReflectionLazyLoader<T> {
if (eval == null) throw new RuntimeException("Returned value is null");
} catch (Throwable throwable) {
if (strict) {
warn(" ----- REFLECTION FAILURE DEBUG INFORMATION, REPORT THIS ON THE ZNPCSPLUS GITHUB ----- ");
warn(getClass().getSimpleName() + " failed!");
warn("Class Names: " + possibleClassNames);
warn("Reflection Type: " + getClass().getCanonicalName());
warn("Bukkit Version: " + VersionUtil.BUKKIT_VERSION + " (" + VersionUtil.getBukkitPackage() + ")");
printDebugInfo(this::warn);
warn("Exception:");
logger.warning(" ----- REFLECTION FAILURE DEBUG INFORMATION, REPORT THIS ON THE ZNPCSPLUS GITHUB ----- ");
logger.warning(getClass().getSimpleName() + " failed!");
logger.warning("Class Names: " + possibleClassNames);
logger.warning("Reflection Type: " + getClass().getCanonicalName());
logger.warning("Bukkit Version: " + VersionUtil.BUKKIT_VERSION + " (" + VersionUtil.getBukkitPackage() + ")");
printDebugInfo(logger::warning);
logger.warning("Exception:");
throwable.printStackTrace();
warn(" ----- REFLECTION FAILURE DEBUG INFORMATION, REPORT THIS ON THE ZNPCSPLUS GITHUB ----- ");
logger.warning(" ----- REFLECTION FAILURE DEBUG INFORMATION, REPORT THIS ON THE ZNPCSPLUS GITHUB ----- ");
}
}
this.loaded = true;
return this.cached;
}
private void warn(String message) {
ZNpcsPlus.LOGGER.warning("[Reflection] " + message);
}
protected abstract T load() throws Exception;
protected void printDebugInfo(Consumer<String> logger) {}
}

View file

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.skin;
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.skin.cache.SkinCache;
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
@ -17,10 +18,10 @@ public interface BaseSkinDescriptor extends SkinDescriptor {
boolean supportsInstant(Player player);
String serialize();
static BaseSkinDescriptor deserialize(String str) {
static BaseSkinDescriptor deserialize(SkinCache skinCache, String str) {
String[] arr = str.split(";");
if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor();
else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(arr[1]);
if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor(skinCache);
else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(skinCache, arr[1]);
else if (arr[0].equalsIgnoreCase("prefetched")) {
List<TextureProperty> properties = new ArrayList<>();
for (int i = 0; i < (arr.length - 1) / 3; i++) {

View file

@ -3,8 +3,7 @@ package lol.pyr.znpcsplus.skin.cache;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.config.Configs;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.reflection.Reflections;
import lol.pyr.znpcsplus.skin.Skin;
import org.bukkit.Bukkit;
@ -22,17 +21,26 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class SkinCache {
private final static Map<String, Skin> cache = new ConcurrentHashMap<>();
private final static Map<String, CachedId> idCache = new ConcurrentHashMap<>();
private final static Logger logger = Logger.getLogger("ZNPCsPlus Skin Cache");
public static void cleanCache() {
private final ConfigManager configManager;
private final Map<String, Skin> cache = new ConcurrentHashMap<>();
private final Map<String, CachedId> idCache = new ConcurrentHashMap<>();
public SkinCache(ConfigManager configManager) {
this.configManager = configManager;
}
public void cleanCache() {
for (Map.Entry<String, Skin> entry : cache.entrySet()) if (entry.getValue().isExpired()) cache.remove(entry.getKey());
for (Map.Entry<String, CachedId> entry : idCache.entrySet()) if (entry.getValue().isExpired()) cache.remove(entry.getKey());
}
public static CompletableFuture<Skin> fetchByName(String name) {
public CompletableFuture<Skin> fetchByName(String name) {
Player player = Bukkit.getPlayerExact(name);
if (player != null && player.isOnline()) return CompletableFuture.completedFuture(getFromPlayer(player));
@ -52,8 +60,8 @@ public class SkinCache {
return fetchByUUID(id).join();
}
} catch (IOException exception) {
if (!Configs.config().disableSkinFetcherWarnings()) {
ZNpcsPlus.LOGGER.warning("Failed to uuid from player name:");
if (!configManager.getConfig().disableSkinFetcherWarnings()) {
logger.warning("Failed to uuid from player name:");
exception.printStackTrace();
}
} finally {
@ -63,11 +71,11 @@ public class SkinCache {
});
}
public static CompletableFuture<Skin> fetchByUUID(UUID uuid) {
public CompletableFuture<Skin> fetchByUUID(UUID uuid) {
return fetchByUUID(uuid.toString().replace("-", ""));
}
public static boolean isNameFullyCached(String s) {
public boolean isNameFullyCached(String s) {
String name = s.toLowerCase();
if (!idCache.containsKey(name)) return false;
CachedId id = idCache.get(name);
@ -76,7 +84,7 @@ public class SkinCache {
return !skin.isExpired();
}
public static Skin getFullyCachedByName(String s) {
public Skin getFullyCachedByName(String s) {
String name = s.toLowerCase();
if (!idCache.containsKey(name)) return null;
CachedId id = idCache.get(name);
@ -86,7 +94,7 @@ public class SkinCache {
return skin;
}
public static CompletableFuture<Skin> fetchByUUID(String uuid) {
public CompletableFuture<Skin> fetchByUUID(String uuid) {
Player player = Bukkit.getPlayer(uuid);
if (player != null && player.isOnline()) return CompletableFuture.completedFuture(getFromPlayer(player));
@ -108,8 +116,8 @@ public class SkinCache {
return skin;
}
} catch (IOException exception) {
if (!Configs.config().disableSkinFetcherWarnings()) {
ZNpcsPlus.LOGGER.warning("Failed to fetch skin:");
if (!configManager.getConfig().disableSkinFetcherWarnings()) {
logger.warning("Failed to fetch skin:");
exception.printStackTrace();
}
} finally {
@ -119,7 +127,7 @@ public class SkinCache {
});
}
public static Skin getFromPlayer(Player player) {
public Skin getFromPlayer(Player player) {
try {
Object playerHandle = Reflections.GET_HANDLE_PLAYER_METHOD.get().invoke(player);
GameProfile gameProfile = (GameProfile) Reflections.GET_PROFILE_METHOD.get().invoke(playerHandle, new Object[0]);

View file

@ -1,15 +1,16 @@
package lol.pyr.znpcsplus.skin.cache;
import lol.pyr.znpcsplus.ZNpcsPlus;
import org.bukkit.scheduler.BukkitRunnable;
public class SkinCacheCleanTask extends BukkitRunnable {
public SkinCacheCleanTask() {
ZNpcsPlus.SCHEDULER.runDelayedTimerAsync(this, 1200, 1200);
private final SkinCache skinCache;
public SkinCacheCleanTask(SkinCache skinCache) {
this.skinCache = skinCache;
}
@Override
public void run() {
SkinCache.cleanCache();
skinCache.cleanCache();
}
}

View file

@ -1,40 +1,36 @@
package lol.pyr.znpcsplus.skin.descriptor;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
import lol.pyr.znpcsplus.skin.Skin;
import lol.pyr.znpcsplus.skin.cache.SkinCache;
import me.clip.placeholderapi.PlaceholderAPI;
import lol.pyr.znpcsplus.util.PapiUtil;
import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture;
public class FetchingDescriptor implements BaseSkinDescriptor, SkinDescriptor {
private final SkinCache skinCache;
private final String name;
public FetchingDescriptor(String name) {
public FetchingDescriptor(SkinCache skinCache, String name) {
this.skinCache = skinCache;
this.name = name;
}
@Override
public CompletableFuture<Skin> fetch(Player player) {
return SkinCache.fetchByName(papi(player));
return skinCache.fetchByName(PapiUtil.set(player, name));
}
@Override
public Skin fetchInstant(Player player) {
return SkinCache.getFullyCachedByName(papi(player));
return skinCache.getFullyCachedByName(PapiUtil.set(player, name));
}
@Override
public boolean supportsInstant(Player player) {
return SkinCache.isNameFullyCached(papi(player));
}
private String papi(Player player) {
if (ZNpcsPlus.PLACEHOLDERS_SUPPORTED) return PlaceholderAPI.setPlaceholders(player, name);
return name;
return skinCache.isNameFullyCached(PapiUtil.set(player, name));
}
public String getName() {

View file

@ -9,17 +9,20 @@ import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture;
public class MirrorDescriptor implements BaseSkinDescriptor, SkinDescriptor {
private final SkinCache skinCache;
public MirrorDescriptor() {}
public MirrorDescriptor(SkinCache skinCache) {
this.skinCache = skinCache;
}
@Override
public CompletableFuture<Skin> fetch(Player player) {
return CompletableFuture.completedFuture(SkinCache.getFromPlayer(player));
return CompletableFuture.completedFuture(skinCache.getFromPlayer(player));
}
@Override
public Skin fetchInstant(Player player) {
return SkinCache.getFromPlayer(player);
return skinCache.getFromPlayer(player);
}
@Override

View file

@ -16,8 +16,8 @@ public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor
this.skin = skin;
}
public static CompletableFuture<PrefetchedDescriptor> forPlayer(String name) {
return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(SkinCache.fetchByName(name).join()));
public static CompletableFuture<PrefetchedDescriptor> forPlayer(SkinCache cache, String name) {
return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByName(name).join()));
}
@Override

View file

@ -1,14 +1,20 @@
package lol.pyr.znpcsplus.storage;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.storage.yaml.YamlStorage;
import java.io.File;
public enum NpcStorageType {
YAML {
@Override
public NpcStorage create() {
return new YamlStorage();
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry) {
return new YamlStorage(packetFactory, configManager, actionRegistry, new File(plugin.getDataFolder(), "data"));
}
};
public abstract NpcStorage create();
public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry);
}

View file

@ -1,7 +0,0 @@
package lol.pyr.znpcsplus.storage;
public class SerializationException extends RuntimeException {
public SerializationException(String message) {
super(message);
}
}

View file

@ -1,13 +1,13 @@
package lol.pyr.znpcsplus.storage.yaml;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.NpcActionType;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcTypeImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.storage.NpcStorage;
import lol.pyr.znpcsplus.util.ZLocation;
import net.kyori.adventure.text.minimessage.MiniMessage;
@ -16,28 +16,32 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
public class YamlStorage implements NpcStorage {
private final File npcsFolder;
private final PacketFactory packetFactory;
private final ConfigManager configManager;
private final ActionRegistry actionRegistry;
private final File folder;
public YamlStorage() {
npcsFolder = new File(ZNpcsPlus.PLUGIN_FOLDER, "npcs");
if (!npcsFolder.exists()) npcsFolder.mkdirs();
public YamlStorage(PacketFactory packetFactory, ConfigManager configManager, ActionRegistry actionRegistry, File folder) {
this.packetFactory = packetFactory;
this.configManager = configManager;
this.actionRegistry = actionRegistry;
this.folder = folder;
if (!this.folder.exists()) this.folder.mkdirs();
}
@SuppressWarnings("ConstantConditions")
@Override
public Collection<NpcEntryImpl> loadNpcs() {
File[] files = npcsFolder.listFiles();
File[] files = folder.listFiles();
if (files == null || files.length == 0) return Collections.emptyList();
List<NpcEntryImpl> npcs = new ArrayList<>();
for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
NpcImpl npc = new NpcImpl(config.getString("world"), NpcTypeImpl.byName(config.getString("type")),
NpcImpl npc = new NpcImpl(configManager, packetFactory, config.getString("world"), NpcTypeImpl.byName(config.getString("type")),
deserializeLocation(config.getConfigurationSection("location")));
ConfigurationSection properties = config.getConfigurationSection("properties");
@ -48,16 +52,8 @@ public class YamlStorage implements NpcStorage {
}
}
for (String line : config.getStringList("hologram")) {
npc.getHologram().addLine(MiniMessage.miniMessage().deserialize(line));
}
int amt = config.getInt("action-amount");
for (int i = 1; i <= amt; i++) {
String key = "actions." + i;
npc.addAction(NpcActionType.valueOf(config.getString(key + ".type"))
.deserialize(config.getInt(key + ".cooldown"), config.getString(key + ".argument")));
}
for (String line : config.getStringList("hologram")) npc.getHologram().addLine(MiniMessage.miniMessage().deserialize(line));
for (String s : config.getStringList("actions")) npc.addAction(actionRegistry.deserialize(s));
NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc);
entry.setProcessed(config.getBoolean("is-processed"));
@ -71,7 +67,7 @@ public class YamlStorage implements NpcStorage {
@Override
public void saveNpcs(Collection<NpcEntryImpl> npcs) {
File[] files = npcsFolder.listFiles();
File[] files = folder.listFiles();
if (files != null && files.length != 0) for (File file : files) file.delete();
for (NpcEntryImpl entry : npcs) try {
YamlConfiguration config = new YamlConfiguration();
@ -93,17 +89,12 @@ public class YamlStorage implements NpcStorage {
lines.add(MiniMessage.miniMessage().serialize(line.getText()));
}
config.set("hologram", lines);
config.set("actions", npc.getActions().stream()
.map(actionRegistry::serialize)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
int i = 0;
for (NpcAction action : npc.getActions()) { i++;
String key = "actions." + i;
config.set(key + ".type", action.getType().name());
config.set(key + ".cooldown", action.getCooldown());
config.set(key + ".argument", action.getArgument());
}
config.set("action-amount", i);
config.save(new File(npcsFolder, entry.getId() + ".yml"));
config.save(new File(folder, entry.getId() + ".yml"));
} catch (IOException e) {
throw new RuntimeException(e);
}

View file

@ -1,7 +1,6 @@
package lol.pyr.znpcsplus.tasks;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.config.Configs;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
@ -11,13 +10,17 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.NumberConversions;
public class NpcVisibilityTask extends BukkitRunnable {
public NpcVisibilityTask() {
ZNpcsPlus.SCHEDULER.runDelayedTimerAsync(this, 60L, 10L);
private final NpcRegistryImpl npcRegistry;
private final ConfigManager configManager;
public NpcVisibilityTask(NpcRegistryImpl npcRegistry, ConfigManager configManager) {
this.npcRegistry = npcRegistry;
this.configManager = configManager;
}
public void run() {
double distSq = NumberConversions.square(Configs.config().viewDistance());
for (NpcEntryImpl entry : NpcRegistryImpl.get().all()) {
double distSq = NumberConversions.square(configManager.getConfig().viewDistance());
for (NpcEntryImpl entry : npcRegistry.all()) {
if (!entry.isProcessed()) continue;
NpcImpl npc = entry.getNpc();
for (Player player : Bukkit.getOnlinePlayers()) {

View file

@ -1,24 +1,26 @@
package lol.pyr.znpcsplus.updater;
import lol.pyr.znpcsplus.ZNpcsPlus;
import me.robertlit.spigotresources.api.Resource;
import me.robertlit.spigotresources.api.SpigotResourcesAPI;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class UpdateChecker extends BukkitRunnable {
private final static SpigotResourcesAPI api = new SpigotResourcesAPI(1, TimeUnit.MINUTES);
public final static int RESOURCE_ID = 109380;
private final static Logger logger = Logger.getLogger("ZNPCsPlus Update Checker");
private final static int RESOURCE_ID = 109380;
public final static String DOWNLOAD_LINK = "https://www.spigotmc.org/resources/znpcsplus.109380/";
private final ZNpcsPlus plugin;
private final SpigotResourcesAPI api = new SpigotResourcesAPI(1, TimeUnit.MINUTES);
private final PluginDescriptionFile info;
private Status status = Status.UNKNOWN;
private String newestVersion = "N/A";
public UpdateChecker(ZNpcsPlus plugin) {
this.plugin = plugin;
ZNpcsPlus.SCHEDULER.runDelayedTimerAsync(this, 5L, 6000L);
public UpdateChecker(PluginDescriptionFile info) {
this.info = info;
}
public void run() {
@ -26,7 +28,7 @@ public class UpdateChecker extends BukkitRunnable {
if (resource == null) return;
newestVersion = resource.getVersion();
int current = versionToNumber(plugin.getDescription().getVersion());
int current = versionToNumber(info.getVersion());
int newest = versionToNumber(newestVersion);
status = current >= newest ? Status.LATEST_VERSION : Status.UPDATE_NEEDED;
@ -34,8 +36,8 @@ public class UpdateChecker extends BukkitRunnable {
}
private void notifyConsole() {
ZNpcsPlus.LOGGER.warning("Version " + getLatestVersion() + " of " + plugin.getDescription().getName() + " is available now!");
ZNpcsPlus.LOGGER.warning("Download it at " + UpdateChecker.DOWNLOAD_LINK);
logger.warning("Version " + getLatestVersion() + " of " + info.getName() + " is available now!");
logger.warning("Download it at " + UpdateChecker.DOWNLOAD_LINK);
}
private int versionToNumber(String version) {

View file

@ -1,6 +1,7 @@
package lol.pyr.znpcsplus.updater;
import lol.pyr.znpcsplus.ZNpcsPlus;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
@ -11,12 +12,13 @@ import org.bukkit.event.player.PlayerJoinEvent;
public class UpdateNotificationListener implements Listener {
private final ZNpcsPlus plugin;
private final BukkitAudiences adventure;
private final UpdateChecker updateChecker;
public UpdateNotificationListener(ZNpcsPlus plugin, UpdateChecker updateChecker) {
public UpdateNotificationListener(ZNpcsPlus plugin, BukkitAudiences adventure, UpdateChecker updateChecker) {
this.plugin = plugin;
this.adventure = adventure;
this.updateChecker = updateChecker;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler
@ -25,7 +27,7 @@ public class UpdateNotificationListener implements Listener {
if (updateChecker.getStatus() != UpdateChecker.Status.UPDATE_NEEDED) return;
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (!event.getPlayer().isOnline()) return;
ZNpcsPlus.ADVENTURE.player(event.getPlayer())
adventure.player(event.getPlayer())
.sendMessage(Component.text(plugin.getDescription().getName() + " v" + updateChecker.getLatestVersion() + " is available now!", NamedTextColor.GOLD).appendNewline()
.append(Component.text("Click this message to open the Spigot page (CLICK)", NamedTextColor.YELLOW)).clickEvent(ClickEvent.openUrl(UpdateChecker.DOWNLOAD_LINK)));
}, 100L);

View file

@ -1,6 +1,6 @@
package lol.pyr.znpcsplus.user;
import lol.pyr.znpcsplus.interaction.NpcAction;
import lol.pyr.znpcsplus.interaction.InteractionAction;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -9,24 +9,6 @@ import java.util.Map;
import java.util.UUID;
public class User {
private final static Map<UUID, User> USER_MAP = new HashMap<>();
public static User get(Player player) {
return get(player.getUniqueId());
}
public static User get(UUID uuid) {
return USER_MAP.computeIfAbsent(uuid, User::new);
}
public static void remove(Player player) {
remove(player.getUniqueId());
}
public static void remove(UUID uuid) {
USER_MAP.remove(uuid);
}
private final UUID uuid;
private long lastNpcInteraction;
private final Map<UUID, Long> actionCooldownMap = new HashMap<>();
@ -51,7 +33,7 @@ public class User {
return uuid;
}
public boolean actionCooldownCheck(NpcAction action) {
public boolean actionCooldownCheck(InteractionAction action) {
UUID id = action.getUuid();
if (System.currentTimeMillis() - actionCooldownMap.getOrDefault(id, 0L) >= action.getCooldown()) {
actionCooldownMap.put(id, System.currentTimeMillis());

View file

@ -1,24 +1,24 @@
package lol.pyr.znpcsplus.user;
import lol.pyr.znpcsplus.ZNpcsPlus;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class UserListener implements Listener {
public UserListener(ZNpcsPlus plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
private final UserManager manager;
public UserListener(UserManager manager) {
this.manager = manager;
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
User.get(event.getPlayer());
manager.get(event.getPlayer());
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
User.remove(event.getPlayer().getUniqueId());
manager.remove(event.getPlayer().getUniqueId());
}
}

View file

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.user;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class UserManager {
private final Map<UUID, User> userMap = new HashMap<>();
public User get(Player player) {
return get(player.getUniqueId());
}
public User get(UUID uuid) {
return userMap.computeIfAbsent(uuid, User::new);
}
public void remove(Player player) {
remove(player.getUniqueId());
}
public void remove(UUID uuid) {
userMap.remove(uuid);
}
}

View file

@ -1,6 +1,5 @@
package lol.pyr.znpcsplus.util;
import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.reflection.Reflections;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
@ -24,7 +23,7 @@ public class FoliaUtil {
else try {
Reflections.FOLIA_TELEPORT_ASYNC.get().invoke(entity, location);
} catch (IllegalAccessException | InvocationTargetException e) {
ZNpcsPlus.LOGGER.severe("Error while teleporting entity:");
System.err.println("Error while teleporting entity:");
e.printStackTrace();
}
}

View file

@ -0,0 +1,19 @@
package lol.pyr.znpcsplus.util;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class PapiUtil {
private static boolean isSupported() {
return Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
}
public static String set(String str) {
return set(null, str);
}
public static String set(Player player, String str) {
return isSupported() ? PlaceholderAPI.setPlaceholders(player, str) : str;
}
}

View file

@ -0,0 +1,6 @@
package lol.pyr.znpcsplus.util;
public interface StringSerializer<T> {
String serialize(T obj);
T deserialize(String str);
}

View file

@ -1,7 +0,0 @@
package lol.pyr.znpcsplus.util;
public class StringUtil {
public static boolean startsWithIgnoreCase(String s1, String s2) {
return s1.toLowerCase().startsWith(s2.toLowerCase());
}
}