upstream #1
60 changed files with 1454 additions and 287 deletions
18
.drone.yml
Normal file
18
.drone.yml
Normal file
|
@ -0,0 +1,18 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- custom
|
||||
|
||||
steps:
|
||||
- name: publish
|
||||
pull: if-not-exists
|
||||
image: openjdk:21-jdk
|
||||
environment:
|
||||
PACKAGESKEY:
|
||||
from_secret: GITEA_PACKAGE_PUBLIC_RW
|
||||
commands:
|
||||
- ./gradlew --no-daemon --parallel -Pnetherite.git.packages.token=$PACKAGESKEY build publish
|
|
@ -40,7 +40,7 @@ Open an issue in the GitHub [issue tracker](https://github.com/Pyrbu/ZNPCsPlus/i
|
|||
|
||||
## Credits
|
||||
- [PacketEvents 2.0](https://github.com/retrooper/packetevents) - Packet library
|
||||
- [wiki.vg](https://wiki.vg/Main_Page) - Minecraft protocol documentation
|
||||
- [Minecraft Wiki Protocol (formally wiki.vg)](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Main_Page) - Minecraft protocol documentation
|
||||
- [gson](https://github.com/google/gson) - JSON parsing library made by Google
|
||||
- [Mineskin.org](https://mineskin.org/) - Website for raw skin file uploads
|
||||
- [adventure](https://docs.advntr.dev/) - Minecraft text api
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
plugins {
|
||||
id "java"
|
||||
id "maven-publish"
|
||||
}
|
||||
|
||||
java {
|
||||
|
@ -21,15 +20,4 @@ publishing {
|
|||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
Map<String, String> systemProperties = System.getenv()
|
||||
credentials {
|
||||
if (systemProperties.containsKey("DIST_USERNAME")) username systemProperties.get("DIST_USERNAME")
|
||||
if (systemProperties.containsKey("DIST_PASSWORD")) password systemProperties.get("DIST_PASSWORD")
|
||||
}
|
||||
// If the BUILD_ID enviroment variable is present that means its a Jenkins build & that it should go into the snapshots repo
|
||||
url = systemProperties.containsKey("BUILD_ID") ? uri("https://repo.pyr.lol/snapshots/") : uri("https://repo.pyr.lol/releases/")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import lol.pyr.znpcsplus.api.interaction.ActionFactory;
|
|||
import lol.pyr.znpcsplus.api.interaction.ActionRegistry;
|
||||
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
|
||||
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
|
||||
import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry;
|
||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
|
||||
|
||||
/**
|
||||
|
@ -46,4 +47,10 @@ public interface NpcApi {
|
|||
* @return the skin descriptor factory
|
||||
*/
|
||||
SkinDescriptorFactory getSkinDescriptorFactory();
|
||||
|
||||
/**
|
||||
* Gets the npc serializer registry.
|
||||
* @return the npc serializer registry
|
||||
*/
|
||||
NpcSerializerRegistry getNpcSerializerRegistry();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.api.interaction;
|
|||
|
||||
public interface ActionRegistry {
|
||||
void register(InteractionActionType<?> type);
|
||||
|
||||
void unregister(Class<? extends InteractionAction> clazz);
|
||||
<T extends InteractionAction> T deserialize(String str);
|
||||
<T extends InteractionAction> String serialize(T action);
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import lol.pyr.znpcsplus.api.interaction.InteractionAction;
|
|||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Base class for all NPCs
|
||||
|
@ -135,14 +137,16 @@ public interface Npc extends PropertyHolder {
|
|||
/**
|
||||
* Shows this NPC to a player
|
||||
* @param player The {@link Player} to show to
|
||||
* @return A future that completes when the npc is fully shown to the player
|
||||
*/
|
||||
void show(Player player);
|
||||
CompletableFuture<Void> show(Player player);
|
||||
|
||||
/**
|
||||
* Respawns this NPC for a player
|
||||
* @param player The {@link Player} to respawn for
|
||||
* @return A future that completes when the npc is fully respawned
|
||||
*/
|
||||
void respawn(Player player);
|
||||
CompletableFuture<Void> respawn(Player player);
|
||||
|
||||
/**
|
||||
* Sets the head rotation of this NPC for a player
|
||||
|
@ -174,4 +178,35 @@ public interface Npc extends PropertyHolder {
|
|||
* @param offHand Should the hand be the offhand
|
||||
*/
|
||||
void swingHand(boolean offHand);
|
||||
|
||||
/**
|
||||
* Gets the passengers of this npc
|
||||
* @return The list of entity ids of the passengers
|
||||
*/
|
||||
|
||||
@Nullable List<Integer> getPassengers();
|
||||
|
||||
/**
|
||||
* Adds a passenger to this npc
|
||||
* @param entityId The entity id of the passenger to add
|
||||
*/
|
||||
void addPassenger(int entityId);
|
||||
|
||||
/**
|
||||
* Removes a passenger from this npc
|
||||
* @param entityId The entity id of the passenger to remove
|
||||
*/
|
||||
void removePassenger(int entityId);
|
||||
|
||||
/**
|
||||
* Gets the vehicle entity id of this npc
|
||||
* @return The entity id of the vehicle
|
||||
*/
|
||||
@Nullable Integer getVehicleId();
|
||||
|
||||
/**
|
||||
* Sets the vehicle id of this npc
|
||||
* @param vehicleId The entity id of the vehicle
|
||||
*/
|
||||
void setVehicleId(Integer vehicleId);
|
||||
}
|
||||
|
|
|
@ -64,4 +64,21 @@ public interface NpcRegistry {
|
|||
* @param id The ID of the NPC entry
|
||||
*/
|
||||
void delete(String id);
|
||||
|
||||
/**
|
||||
* Register an NPC to this registry
|
||||
* NpcEntry instances can be obtained through the NpcSerializer classes
|
||||
* @param entry The npc to be registered
|
||||
*/
|
||||
void register(NpcEntry entry);
|
||||
|
||||
/**
|
||||
* Reload all saveable npcs from storage
|
||||
*/
|
||||
void reload();
|
||||
|
||||
/**
|
||||
* Save all saveable npcs to storage
|
||||
*/
|
||||
void save();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package lol.pyr.znpcsplus.api.serialization;
|
||||
|
||||
import lol.pyr.znpcsplus.api.npc.NpcEntry;
|
||||
|
||||
public interface NpcSerializer<T> {
|
||||
/**
|
||||
* Serialize an npc into the type of this serializer
|
||||
* @param entry The npc entry
|
||||
* @return The serialized class
|
||||
*/
|
||||
T serialize(NpcEntry entry);
|
||||
|
||||
/**
|
||||
* Deserialize an npc from a serialized class
|
||||
* Note: This npc will not be registered, you need to also register it using the NpcRegistry#register(NpcEntry) method
|
||||
* @param model The serialized class
|
||||
* @return The deserialized NpcEntry
|
||||
*/
|
||||
NpcEntry deserialize(T model);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package lol.pyr.znpcsplus.api.serialization;
|
||||
|
||||
public interface NpcSerializerRegistry {
|
||||
/**
|
||||
* Get an NpcSerializer that serializes npcs into the provided class
|
||||
* @param clazz The class to serialize into
|
||||
* @return The npc serializer instance
|
||||
* @param <T> The type of the class that the serializer serializes into
|
||||
*/
|
||||
<T> NpcSerializer<T> getSerializer(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Register an NpcSerializer to be used by other plugins
|
||||
* @param clazz The class that the serializer serializes into
|
||||
* @param serializer The serializer itself
|
||||
* @param <T> The type of the class that the serializer serializes into
|
||||
*/
|
||||
<T> void registerSerializer(Class<T> clazz, NpcSerializer<T> serializer);
|
||||
}
|
|
@ -12,4 +12,5 @@ public interface SkinDescriptorFactory {
|
|||
SkinDescriptor createStaticDescriptor(String texture, String signature);
|
||||
SkinDescriptor createUrlDescriptor(String url, String variant);
|
||||
SkinDescriptor createUrlDescriptor(URL url, String variant);
|
||||
SkinDescriptor createFileDescriptor(String path);
|
||||
}
|
||||
|
|
11
api/src/main/java/lol/pyr/znpcsplus/util/SkeletonType.java
Normal file
11
api/src/main/java/lol/pyr/znpcsplus/util/SkeletonType.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package lol.pyr.znpcsplus.util;
|
||||
|
||||
public enum SkeletonType {
|
||||
NORMAL,
|
||||
WITHER,
|
||||
STRAY;
|
||||
|
||||
public byte getLegacyId() {
|
||||
return (byte) ordinal();
|
||||
}
|
||||
}
|
26
build.gradle
26
build.gradle
|
@ -2,13 +2,14 @@ subprojects {
|
|||
apply plugin: "java"
|
||||
|
||||
group "lol.pyr"
|
||||
version "2.0.0" + (System.getenv().containsKey("BUILD_ID") ? "-SNAPSHOT" : "")
|
||||
version "2.0.0-netherite-SNAPSHOT"
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "org.jetbrains:annotations:26.0.1"
|
||||
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
|
||||
}
|
||||
|
||||
|
@ -20,6 +21,9 @@ subprojects {
|
|||
maven {
|
||||
url "https://repo.codemc.io/repository/maven-releases/"
|
||||
}
|
||||
maven {
|
||||
url "https://repo.codemc.io/repository/maven-snapshots/"
|
||||
}
|
||||
maven {
|
||||
url "https://libraries.minecraft.net"
|
||||
}
|
||||
|
@ -32,5 +36,23 @@ subprojects {
|
|||
maven {
|
||||
url "https://repo.pyr.lol/releases"
|
||||
}
|
||||
maven {
|
||||
url "https://jitpack.io/"
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://git.netherite.gg/api/packages/Netherite-Public/maven")
|
||||
credentials(HttpHeaderCredentials) {
|
||||
name "Authorization"
|
||||
value "token ${project.properties["netherite.git.packages.token"]}"
|
||||
}
|
||||
authentication {
|
||||
header(HttpHeaderAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
0
gradlew
vendored
Normal file → Executable file
0
gradlew
vendored
Normal file → Executable file
|
@ -8,32 +8,65 @@ runServer {
|
|||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
minecraftVersion "1.21.3"
|
||||
minecraftVersion "1.21.4"
|
||||
}
|
||||
|
||||
processResources {
|
||||
expand("version": version)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifactId = "znpcsplus-plugin"
|
||||
|
||||
pom {
|
||||
name.set("znpcsplus-plugin")
|
||||
description.set("The ZNPCsPlus plugin")
|
||||
url.set("https://github.com/Pyrbu/ZNPCsPlus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "me.clip:placeholderapi:2.11.6" // Placeholder support
|
||||
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
|
||||
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
|
||||
implementation "com.github.retrooper:packetevents-spigot:2.6.0" // Packets
|
||||
implementation "com.github.retrooper:packetevents-spigot:2.7.0" // Packets
|
||||
implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
|
||||
implementation "lol.pyr:director-adventure:2.1.2" // Commands
|
||||
|
||||
// Fancy text library
|
||||
implementation "net.kyori:adventure-platform-bukkit:4.3.4"
|
||||
implementation "net.kyori:adventure-text-minimessage:4.17.0"
|
||||
implementation "net.kyori:adventure-text-minimessage:4.18.0"
|
||||
|
||||
implementation project(":api")
|
||||
}
|
||||
|
||||
ext {
|
||||
gitBranch = System.getenv('GIT_BRANCH') ?: ''
|
||||
gitCommitHash = System.getenv('GIT_COMMIT') ?: ''
|
||||
buildId = System.getenv('BUILD_ID') ?: ''
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archivesBaseName = "ZNPCsPlus"
|
||||
archiveClassifier.set ""
|
||||
|
||||
manifest {
|
||||
if (gitBranch?.trim()) {
|
||||
attributes('Git-Branch': gitBranch)
|
||||
}
|
||||
if (gitCommitHash?.trim()) {
|
||||
attributes('Git-Commit': gitCommitHash)
|
||||
}
|
||||
if (buildId?.trim()) {
|
||||
attributes('Build-Id': buildId)
|
||||
}
|
||||
}
|
||||
|
||||
relocate "org.objectweb.asm", "lol.pyr.znpcsplus.libraries.asm"
|
||||
relocate "me.lucko.jarrelocator", "lol.pyr.znpcsplus.libraries.jarrelocator"
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ import lol.pyr.znpcsplus.parsers.*;
|
|||
import lol.pyr.znpcsplus.scheduling.FoliaScheduler;
|
||||
import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
|
||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||
|
@ -93,7 +94,7 @@ public class ZNpcsPlus {
|
|||
packetEvents.load();
|
||||
|
||||
configManager = new ConfigManager(getDataFolder());
|
||||
skinCache = new MojangSkinCache(configManager);
|
||||
skinCache = new MojangSkinCache(configManager, new File(getDataFolder(), "skins"));
|
||||
propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
|
||||
|
||||
NpcPropertyRegistryProvider.register(propertyRegistry);
|
||||
|
@ -135,8 +136,9 @@ public class ZNpcsPlus {
|
|||
ActionRegistryImpl actionRegistry = new ActionRegistryImpl();
|
||||
ActionFactoryImpl actionFactory = new ActionFactoryImpl(scheduler, adventure, textSerializer, bungeeConnector);
|
||||
NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
|
||||
NpcSerializerRegistryImpl serializerRegistry = new NpcSerializerRegistryImpl(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
NpcRegistryImpl npcRegistry = new NpcRegistryImpl(configManager, this, packetFactory, actionRegistry,
|
||||
scheduler, typeRegistry, propertyRegistry, textSerializer);
|
||||
scheduler, typeRegistry, propertyRegistry, serializerRegistry, textSerializer);
|
||||
shutdownTasks.add(npcRegistry::unload);
|
||||
|
||||
UserManager userManager = new UserManager();
|
||||
|
@ -159,7 +161,7 @@ public class ZNpcsPlus {
|
|||
pluginManager.registerEvents(new UserListener(userManager), bootstrap);
|
||||
|
||||
registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
|
||||
typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory);
|
||||
typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory, serializerRegistry);
|
||||
|
||||
log(ChatColor.WHITE + " * Starting tasks...");
|
||||
if (configManager.getConfig().checkForUpdates()) {
|
||||
|
@ -193,7 +195,7 @@ public class ZNpcsPlus {
|
|||
}
|
||||
}
|
||||
|
||||
NpcApiProvider.register(bootstrap, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, actionRegistry, actionFactory, skinCache));
|
||||
NpcApiProvider.register(bootstrap, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, actionRegistry, actionFactory, skinCache, serializerRegistry));
|
||||
log(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
|
||||
log("");
|
||||
|
||||
|
@ -246,7 +248,7 @@ public class ZNpcsPlus {
|
|||
private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
|
||||
ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry,
|
||||
EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,
|
||||
ConfigManager configManager, PacketFactory packetFactory) {
|
||||
ConfigManager configManager, PacketFactory packetFactory, NpcSerializerRegistryImpl serializerRegistry) {
|
||||
|
||||
Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED));
|
||||
CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
|
||||
|
@ -296,6 +298,7 @@ public class ZNpcsPlus {
|
|||
registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, SkeletonType.class, incorrectUsageMessage);
|
||||
|
||||
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
|
||||
.addSubcommand("center", new CenterCommand(npcRegistry))
|
||||
|
@ -321,7 +324,7 @@ public class ZNpcsPlus {
|
|||
.addSubcommand("save", new SaveAllCommand(npcRegistry))
|
||||
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
|
||||
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))
|
||||
.addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry)))
|
||||
.addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry, serializerRegistry)))
|
||||
.addSubcommand("holo", new MultiCommand(bootstrap.loadHelpMessage("holo"))
|
||||
.addSubcommand("add", new HoloAddCommand(npcRegistry))
|
||||
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
|
||||
|
@ -332,13 +335,16 @@ public class ZNpcsPlus {
|
|||
.addSubcommand("set", new HoloSetCommand(npcRegistry))
|
||||
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
|
||||
.addSubcommand("offset", new HoloOffsetCommand(npcRegistry))
|
||||
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry)))
|
||||
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry))
|
||||
.addSubcommand("removeduplicate", new HoloRemoveDuplicateCommand(npcRegistry))
|
||||
.addSubcommand("removeallduplicates", new holoRemoveAllDuplicatesCommand(npcRegistry)))
|
||||
.addSubcommand("action", new MultiCommand(bootstrap.loadHelpMessage("action"))
|
||||
.addSubcommand("add", new ActionAddCommand(npcRegistry, actionRegistry))
|
||||
.addSubcommand("clear", new ActionClearCommand(npcRegistry))
|
||||
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
|
||||
.addSubcommand("edit", new ActionEditCommand(npcRegistry, actionRegistry))
|
||||
.addSubcommand("list", new ActionListCommand(npcRegistry)))
|
||||
.addSubcommand("version", new VersionCommand(this))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import lol.pyr.znpcsplus.interaction.ActionFactoryImpl;
|
|||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.skin.SkinDescriptorFactoryImpl;
|
||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||
|
||||
|
@ -22,14 +23,16 @@ public class ZNpcsPlusApi implements NpcApi {
|
|||
private final ActionRegistryImpl actionRegistry;
|
||||
private final ActionFactoryImpl actionFactory;
|
||||
private final SkinDescriptorFactoryImpl skinDescriptorFactory;
|
||||
private final NpcSerializerRegistryImpl npcSerializerRegistry;
|
||||
|
||||
public ZNpcsPlusApi(NpcRegistryImpl npcRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, ActionRegistryImpl actionRegistry, ActionFactoryImpl actionFactory, MojangSkinCache skinCache) {
|
||||
public ZNpcsPlusApi(NpcRegistryImpl npcRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, ActionRegistryImpl actionRegistry, ActionFactoryImpl actionFactory, MojangSkinCache skinCache, NpcSerializerRegistryImpl npcSerializerRegistry) {
|
||||
this.npcRegistry = npcRegistry;
|
||||
this.typeRegistry = typeRegistry;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.actionFactory = actionFactory;
|
||||
this.skinDescriptorFactory = new SkinDescriptorFactoryImpl(skinCache);
|
||||
this.npcSerializerRegistry = npcSerializerRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,4 +65,9 @@ public class ZNpcsPlusApi implements NpcApi {
|
|||
public SkinDescriptorFactory getSkinDescriptorFactory() {
|
||||
return skinDescriptorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpcSerializerRegistryImpl getNpcSerializerRegistry() {
|
||||
return npcSerializerRegistry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,18 @@ public class CreateCommand implements CommandHandler {
|
|||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " create <id> <type>");
|
||||
context.setUsage(context.getLabel() + " create <id> [<type>]");
|
||||
Player player = context.ensureSenderIsPlayer();
|
||||
|
||||
String id = context.popString();
|
||||
if (npcRegistry.getById(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED));
|
||||
NpcTypeImpl type = context.parse(NpcTypeImpl.class);
|
||||
|
||||
NpcTypeImpl type;
|
||||
if (context.argSize() == 1) {
|
||||
type = context.parse(NpcTypeImpl.class);
|
||||
} else {
|
||||
type = typeRegistry.getByName("player");
|
||||
}
|
||||
|
||||
NpcEntryImpl entry = npcRegistry.create(id, player.getWorld(), type, new NpcLocation(player.getLocation()));
|
||||
entry.enableEverything();
|
||||
|
|
|
@ -17,10 +17,14 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
|||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SkinCommand implements CommandHandler {
|
||||
private final MojangSkinCache skinCache;
|
||||
|
@ -90,6 +94,30 @@ public class SkinCommand implements CommandHandler {
|
|||
context.send(Component.text("Invalid url!", NamedTextColor.RED));
|
||||
}
|
||||
return;
|
||||
} else if (type.equalsIgnoreCase("file")) {
|
||||
context.ensureArgsNotEmpty();
|
||||
String path = context.dumpAllArgs();
|
||||
context.send(Component.text("Fetching skin from file \"" + path + "\"...", NamedTextColor.GREEN));
|
||||
PrefetchedDescriptor.fromFile(skinCache, path).exceptionally(e -> {
|
||||
if (e instanceof FileNotFoundException || e.getCause() instanceof FileNotFoundException) {
|
||||
context.send(Component.text("A file at the specified path could not be found!", NamedTextColor.RED));
|
||||
} else {
|
||||
context.send(Component.text("An error occurred while fetching the skin from file! Check the console for more details.", NamedTextColor.RED));
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}).thenAccept(skin -> {
|
||||
if (skin == null) return;
|
||||
if (skin.getSkin() == null) {
|
||||
context.send(Component.text("Failed to fetch skin, are you sure the file path is valid?", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin);
|
||||
npc.respawn();
|
||||
context.send(Component.text("The NPC's skin has been set.", NamedTextColor.GREEN));
|
||||
});
|
||||
return;
|
||||
}
|
||||
context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic, url"));
|
||||
}
|
||||
|
@ -97,11 +125,19 @@ public class SkinCommand implements CommandHandler {
|
|||
@Override
|
||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
|
||||
if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic", "url");
|
||||
if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic", "url", "file");
|
||||
if (context.matchSuggestion("*", "static")) return context.suggestPlayers();
|
||||
if (context.argSize() == 3 && context.matchSuggestion("*", "url")) {
|
||||
return context.suggestLiteral("slim", "classic");
|
||||
}
|
||||
if (context.argSize() == 3 && context.matchSuggestion("*", "file")) {
|
||||
if (skinCache.getSkinsFolder().exists()) {
|
||||
File[] files = skinCache.getSkinsFolder().listFiles();
|
||||
if (files != null) {
|
||||
return Arrays.stream(files).map(File::getName).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,14 @@ public class ToggleCommand implements CommandHandler {
|
|||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " toggle <id>");
|
||||
context.setUsage(context.getLabel() + " toggle <id> [<enable/disable>]");
|
||||
NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
|
||||
boolean enabled = !npc.isEnabled();
|
||||
boolean enabled;
|
||||
if (context.argSize() == 1) {
|
||||
enabled = context.popString().equalsIgnoreCase("enable");
|
||||
} else {
|
||||
enabled = !npc.isEnabled();
|
||||
}
|
||||
npc.setEnabled(enabled);
|
||||
context.send(Component.text("NPC has been " + (enabled ? "enabled" : "disabled"), NamedTextColor.GREEN));
|
||||
}
|
||||
|
@ -31,6 +36,7 @@ public class ToggleCommand implements CommandHandler {
|
|||
@Override
|
||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
|
||||
if (context.argSize() == 2) return context.suggestLiteral("enable", "disable");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
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 net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class VersionCommand implements CommandHandler {
|
||||
|
||||
private final String pluginVersion;
|
||||
private final String gitBranch;
|
||||
private final String gitCommitHash;
|
||||
private final String buildId;
|
||||
|
||||
public VersionCommand(ZNpcsPlus plugin) {
|
||||
pluginVersion = plugin.getDescription().getVersion();
|
||||
String gitBranch = "";
|
||||
String gitCommitHash = "";
|
||||
String buildId = "";
|
||||
try {
|
||||
URL jarUrl = getClass().getProtectionDomain().getCodeSource().getLocation();
|
||||
JarFile jarFile = new JarFile(jarUrl.toURI().getPath());
|
||||
Attributes attributes = jarFile.getManifest().getMainAttributes();
|
||||
gitBranch = attributes.getValue("Git-Branch");
|
||||
gitCommitHash = attributes.getValue("Git-Commit-Hash");
|
||||
buildId = attributes.getValue("Build-Id");
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.gitBranch = gitBranch;
|
||||
this.gitCommitHash = gitCommitHash;
|
||||
this.buildId = buildId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
|
||||
StringBuilder versionBuilder = new StringBuilder("This server is running ZNPCsPlus version ").append(pluginVersion);
|
||||
if (gitBranch != null && !gitBranch.isEmpty()) {
|
||||
versionBuilder.append("-").append(gitBranch);
|
||||
}
|
||||
if (gitCommitHash != null && !gitCommitHash.isEmpty()) {
|
||||
versionBuilder.append("@").append(gitCommitHash);
|
||||
}
|
||||
if (buildId != null && !buildId.isEmpty()) {
|
||||
versionBuilder.append(" (Build #").append(buildId).append(")");
|
||||
}
|
||||
|
||||
String version = versionBuilder.toString();
|
||||
|
||||
context.send(Component.text(version, NamedTextColor.GREEN)
|
||||
.hoverEvent(Component.text("Click to copy version to clipboard"))
|
||||
.clickEvent(ClickEvent.copyToClipboard(version)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
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.hologram.HologramImpl;
|
||||
import lol.pyr.znpcsplus.hologram.HologramLine;
|
||||
import lol.pyr.znpcsplus.hologram.HologramText;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class HoloRemoveDuplicateCommand implements CommandHandler {
|
||||
private final NpcRegistryImpl registry;
|
||||
|
||||
public HoloRemoveDuplicateCommand(NpcRegistryImpl registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " holo removeduplicates <id>");
|
||||
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
|
||||
List<HologramLine<?>> lines = new ArrayList<>(hologram.getLines());
|
||||
List<HologramText> textLines = new ArrayList<>();
|
||||
|
||||
Iterator<HologramLine<?>> iterator = lines.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
HologramLine<?> line = iterator.next();
|
||||
if (line instanceof HologramText textLine
|
||||
&& !textLine.getValue().equals(Component.empty())
|
||||
&& textLines.contains(textLine)) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line instanceof HologramText textLine) {
|
||||
textLines.add(textLine);
|
||||
}
|
||||
}
|
||||
|
||||
hologram.clearLines();
|
||||
hologram.addLines(lines);
|
||||
context.send(Component.text("NPC lines fixed!", NamedTextColor.GREEN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||
if (context.argSize() == 1) return context.suggestCollection(registry.getModifiableIds());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
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.hologram.HologramImpl;
|
||||
import lol.pyr.znpcsplus.hologram.HologramLine;
|
||||
import lol.pyr.znpcsplus.hologram.HologramText;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class holoRemoveAllDuplicatesCommand implements CommandHandler {
|
||||
private final NpcRegistryImpl registry;
|
||||
|
||||
public holoRemoveAllDuplicatesCommand(NpcRegistryImpl registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " holo removeallduplicates <id>");
|
||||
|
||||
for (NpcEntryImpl npcEntry : registry.getAll()) {
|
||||
HologramImpl hologram = npcEntry.getNpc().getHologram();
|
||||
List<HologramLine<?>> lines = new ArrayList<>(hologram.getLines());
|
||||
List<HologramText> textLines = new ArrayList<>();
|
||||
|
||||
Iterator<HologramLine<?>> iterator = lines.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
HologramLine<?> line = iterator.next();
|
||||
if (line instanceof HologramText textLine
|
||||
&& !textLine.getValue().equals(Component.empty())
|
||||
&& textLines.contains(textLine)) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line instanceof HologramText textLine) {
|
||||
textLines.add(textLine);
|
||||
}
|
||||
}
|
||||
|
||||
hologram.clearLines();
|
||||
hologram.addLines(lines);
|
||||
}
|
||||
|
||||
context.send(Component.text("NPCs fixed!", NamedTextColor.GREEN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||
if (context.argSize() == 1) return context.suggestCollection(registry.getModifiableIds());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import lol.pyr.director.adventure.command.CommandHandler;
|
|||
import lol.pyr.director.common.command.CommandExecutionException;
|
||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
|
||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
|
@ -124,6 +125,15 @@ public class PropertySetCommand implements CommandHandler {
|
|||
value = context.parse(type);
|
||||
valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString();
|
||||
}
|
||||
else if (property instanceof AttributeProperty) {
|
||||
value = context.parse(type);
|
||||
if ((double) value < ((AttributeProperty) property).getMinValue() || (double) value > ((AttributeProperty) property).getMaxValue()) {
|
||||
double sanitizedValue = ((AttributeProperty) property).sanitizeValue((double) value);
|
||||
context.send(Component.text("WARNING: Value " + value + " is out of range for property " + property.getName() + ", setting to " + sanitizedValue, NamedTextColor.YELLOW));
|
||||
value = sanitizedValue;
|
||||
}
|
||||
valueName = String.valueOf(value);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
value = context.parse(type);
|
||||
|
|
|
@ -7,13 +7,13 @@ import lol.pyr.znpcsplus.conversion.DataImporter;
|
|||
import lol.pyr.znpcsplus.conversion.DataImporterRegistry;
|
||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ImportCommand implements CommandHandler {
|
||||
private final NpcRegistryImpl npcRegistry;
|
||||
|
@ -33,7 +33,7 @@ public class ImportCommand implements CommandHandler {
|
|||
if (importer == null) context.halt(Component.text("Importer not found! Possible importers: " +
|
||||
String.join(", ", importerRegistry.getIds()), NamedTextColor.RED));
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||
if (!importer.isValid()) {
|
||||
context.send(Component.text("There is no data to import from this importer!", NamedTextColor.RED));
|
||||
return;
|
||||
|
|
|
@ -4,12 +4,12 @@ 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.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class LoadAllCommand implements CommandHandler {
|
||||
private final NpcRegistryImpl npcRegistry;
|
||||
|
@ -20,7 +20,7 @@ public class LoadAllCommand implements CommandHandler {
|
|||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||
npcRegistry.reload();
|
||||
context.send(Component.text("All NPCs have been re-loaded from storage", NamedTextColor.GREEN));
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
|||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
@ -35,8 +36,9 @@ public class MigrateCommand implements CommandHandler {
|
|||
private final NpcStorage currentStorage;
|
||||
private final NpcStorageType currentStorageType;
|
||||
private final NpcRegistryImpl npcRegistry;
|
||||
private final NpcSerializerRegistryImpl serializerRegistry;
|
||||
|
||||
public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry) {
|
||||
public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry, NpcSerializerRegistryImpl serializerRegistry) {
|
||||
this.configManager = configManager;
|
||||
this.plugin = plugin;
|
||||
this.packetFactory = packetFactory;
|
||||
|
@ -47,6 +49,7 @@ public class MigrateCommand implements CommandHandler {
|
|||
this.currentStorage = currentStorage;
|
||||
this.currentStorageType = currentStorageType;
|
||||
this.npcRegistry = npcRegistry;
|
||||
this.serializerRegistry = serializerRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +66,7 @@ public class MigrateCommand implements CommandHandler {
|
|||
if (currentStorageType == from) {
|
||||
fromStorage = currentStorage;
|
||||
} else {
|
||||
fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
|
||||
if (fromStorage == null) {
|
||||
context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED));
|
||||
return;
|
||||
|
@ -84,7 +87,7 @@ public class MigrateCommand implements CommandHandler {
|
|||
if (currentStorageType == to) {
|
||||
toStorage = currentStorage;
|
||||
} else {
|
||||
toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
|
||||
if (toStorage == null) {
|
||||
context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED));
|
||||
return;
|
||||
|
|
|
@ -4,12 +4,12 @@ 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.npc.NpcRegistryImpl;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SaveAllCommand implements CommandHandler {
|
||||
private final NpcRegistryImpl npcRegistry;
|
||||
|
@ -20,7 +20,7 @@ public class SaveAllCommand implements CommandHandler {
|
|||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||
npcRegistry.save();
|
||||
context.send(Component.text("All NPCs have been saved to storage", NamedTextColor.GREEN));
|
||||
});
|
||||
|
|
|
@ -189,12 +189,15 @@ public class ZNpcImporter implements DataImporter {
|
|||
if (toggleValues.containsKey("mirror")) {
|
||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
|
||||
}
|
||||
if (toggleValues.containsKey("glow")) {
|
||||
try {
|
||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf((String) toggleValues.get("glow")));
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (toggleValues.containsKey("glow") && (boolean) toggleValues.get("glow")) {
|
||||
if (!model.getGlowName().isEmpty())
|
||||
try {
|
||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf(model.getGlowName()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE);
|
||||
}
|
||||
else
|
||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,4 +84,8 @@ public class ZNpcsModel {
|
|||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public String getGlowName() {
|
||||
return glowName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package lol.pyr.znpcsplus.entity;
|
||||
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents an armor stand vehicle entity.
|
||||
* <p>
|
||||
* This entity is used to make the NPC sit on an invisible armor stand.
|
||||
* </p>
|
||||
*/
|
||||
public class ArmorStandVehicleProperties implements PropertyHolder {
|
||||
|
||||
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
|
||||
|
||||
public ArmorStandVehicleProperties(EntityPropertyRegistryImpl propertyRegistry) {
|
||||
_setProperty(propertyRegistry.getByName("small", Boolean.class), true);
|
||||
_setProperty(propertyRegistry.getByName("invisible", Boolean.class), true);
|
||||
_setProperty(propertyRegistry.getByName("base_plate", Boolean.class), false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getProperty(EntityProperty<T> key) {
|
||||
return hasProperty(key) ? (T) propertyMap.get((EntityPropertyImpl<?>) key) : key.getDefaultValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(EntityProperty<?> key) {
|
||||
return propertyMap.containsKey((EntityPropertyImpl<?>) key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void _setProperty(EntityProperty<T> key, T value) {
|
||||
Object val = value;
|
||||
if (val instanceof ItemStack) val = SpigotConversionUtil.fromBukkitItemStack((ItemStack) val);
|
||||
|
||||
setProperty((EntityPropertyImpl<T>) key, (T) val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setProperty(EntityProperty<T> key, T value) {
|
||||
throw new UnsupportedOperationException("Cannot set properties on armor stands");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemProperty(EntityProperty<?> key, ItemStack value) {
|
||||
throw new UnsupportedOperationException("Cannot set item properties on armor stands");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemProperty(EntityProperty<?> key) {
|
||||
throw new UnsupportedOperationException("Cannot get item properties on armor stands");
|
||||
}
|
||||
|
||||
public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
|
||||
if (key == null) return;
|
||||
if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
|
||||
else propertyMap.put(key, value);
|
||||
}
|
||||
|
||||
public Set<EntityProperty<?>> getAllProperties() {
|
||||
return Collections.unmodifiableSet(propertyMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<EntityProperty<?>> getAppliedProperties() {
|
||||
return Collections.unmodifiableSet(propertyMap.keySet());
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.entity;
|
|||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
|
||||
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
|
||||
|
@ -15,6 +16,7 @@ import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
|
|||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.properties.*;
|
||||
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
|
||||
import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty;
|
||||
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
|
||||
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
|
||||
|
@ -32,18 +34,19 @@ import java.util.*;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 1.8 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=7415">...</a>
|
||||
* 1.9 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=7968">...</a>
|
||||
* 1.10 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=8241">...</a>
|
||||
* 1.11 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=8534">...</a>
|
||||
* 1.12 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=14048">...</a>
|
||||
* 1.13 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=14800">...</a>
|
||||
* 1.14 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=15240">...</a>
|
||||
* 1.15 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=15991">...</a>
|
||||
* 1.16 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=16539">...</a>
|
||||
* 1.17 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=17521">...</a>
|
||||
* 1.18-1.19 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=18191">...</a>
|
||||
* 1.20 <a href="https://wiki.vg/index.php?title=Entity_metadata">...</a>
|
||||
* 1.8 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2767708">...</a>
|
||||
* 1.9 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768074">...</a>
|
||||
* 1.10 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768201">...</a>
|
||||
* 1.11 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768444">...</a>
|
||||
* 1.12 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768647">...</a>
|
||||
* 1.13 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768701">...</a>
|
||||
* 1.14 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768716">...</a>
|
||||
* 1.15 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2768877">...</a>
|
||||
* 1.16 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2769100">...</a>
|
||||
* 1.17 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2769318">...</a>
|
||||
* 1.18-1.19 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2769409">...</a>
|
||||
* 1.20 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata?oldid=2769476">...</a>
|
||||
* 1.21 <a href="https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Entity_metadata">...</a>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||
|
@ -90,6 +93,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
registerEnumSerializer(Sound.class);
|
||||
registerEnumSerializer(ArmadilloState.class);
|
||||
registerEnumSerializer(WoldVariant.class);
|
||||
registerEnumSerializer(SkeletonType.class);
|
||||
|
||||
registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.class);
|
||||
|
||||
|
@ -124,6 +128,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
|
||||
register(new DummyProperty<>("look", LookType.FIXED));
|
||||
register(new DummyProperty<>("look_distance", configManager.getConfig().lookPropertyDistance()));
|
||||
register(new DummyProperty<>("look_return", false));
|
||||
register(new DummyProperty<>("view_distance", configManager.getConfig().viewDistance()));
|
||||
|
||||
register(new DummyProperty<>("permission_required", false));
|
||||
|
@ -151,6 +156,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
linkProperties("glow", "fire", "invisible");
|
||||
register(new BooleanProperty("silent", 4, false, legacyBooleans));
|
||||
|
||||
// Attribute Max Health
|
||||
register(new AttributeProperty(packetFactory, "attribute_max_health", Attributes.MAX_HEALTH));
|
||||
|
||||
// Health - LivingEntity
|
||||
int healthIndex = 6;
|
||||
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) healthIndex = 9;
|
||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) healthIndex = 8;
|
||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) healthIndex = 7;
|
||||
register(new HealthProperty(healthIndex));
|
||||
|
||||
final int tameableIndex;
|
||||
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tameableIndex = 17;
|
||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) tameableIndex = 16;
|
||||
|
@ -182,6 +197,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
register(new BooleanProperty("baby", babyIndex, false, legacyBooleans));
|
||||
}
|
||||
|
||||
register(new EntitySittingProperty(packetFactory, this));
|
||||
|
||||
// Player
|
||||
register(new DummyProperty<>("skin", SkinDescriptor.class, false));
|
||||
final int skinLayersIndex;
|
||||
|
@ -436,6 +453,12 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
witherIndex += 3; // skip the first 3 indexes, will be used for the other properties later
|
||||
register(new IntegerProperty("invulnerable_time", witherIndex, 0, false));
|
||||
|
||||
// Skeleton
|
||||
if (ver.isOlderThan(ServerVersion.V_1_11)) {
|
||||
if (legacyBooleans) register(new EncodedByteProperty<>("skeleton_type", SkeletonType.NORMAL, 13, SkeletonType::getLegacyId));
|
||||
else register(new EncodedIntegerProperty<>("skeleton_type", SkeletonType.NORMAL, ver.isOlderThan(ServerVersion.V_1_10) ? 11 : 12, Enum::ordinal));
|
||||
}
|
||||
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
||||
// Shulker
|
||||
int shulkerIndex;
|
||||
|
@ -483,7 +506,9 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) llamaIndex = 20;
|
||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) llamaIndex = 19;
|
||||
else llamaIndex = 17;
|
||||
register(new EncodedIntegerProperty<DyeColor>("carpet_color", DyeColor.class, llamaIndex++, obj -> obj == null ? -1 : obj.ordinal()));
|
||||
|
||||
// Removed in 1.21
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21)) register(new EncodedIntegerProperty<DyeColor>("carpet_color", DyeColor.class, llamaIndex++, obj -> obj == null ? -1 : obj.ordinal()));
|
||||
register(new EncodedIntegerProperty<>("llama_variant", LlamaVariant.CREAMY, llamaIndex, Enum::ordinal));
|
||||
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
|
||||
|
@ -663,8 +688,20 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21)) return;
|
||||
|
||||
register(new EquipmentProperty(packetFactory, "body", EquipmentSlot.BODY));
|
||||
|
||||
// Bogged
|
||||
register(new BooleanProperty("bogged_sheared", 16, false, legacyBooleans));
|
||||
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21_2)) return;
|
||||
|
||||
// Creaking
|
||||
register(new BooleanProperty("creaking_active", 17, false, legacyBooleans));
|
||||
|
||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21_4)) return;
|
||||
|
||||
// Creaking
|
||||
register(new BooleanProperty("creaking_crumbling", 18, false, legacyBooleans));
|
||||
}
|
||||
|
||||
private void registerSerializer(PropertySerializer<?> serializer) {
|
||||
|
|
|
@ -8,27 +8,34 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
|||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.reflection.Reflections;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import lol.pyr.znpcsplus.util.Viewable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class PacketEntity implements PropertyHolder {
|
||||
private final PacketFactory packetFactory;
|
||||
|
||||
private final PropertyHolder properties;
|
||||
private final Viewable viewable;
|
||||
private final int entityId;
|
||||
private final UUID uuid;
|
||||
|
||||
private final EntityType type;
|
||||
private NpcLocation location;
|
||||
|
||||
public PacketEntity(PacketFactory packetFactory, PropertyHolder properties, EntityType type, NpcLocation location) {
|
||||
private PacketEntity vehicle;
|
||||
private Integer vehicleId;
|
||||
private List<Integer> passengers;
|
||||
|
||||
public PacketEntity(PacketFactory packetFactory, PropertyHolder properties, Viewable viewable, EntityType type, NpcLocation location) {
|
||||
this.packetFactory = packetFactory;
|
||||
this.properties = properties;
|
||||
this.viewable = viewable;
|
||||
this.entityId = reserveEntityID();
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.type = type;
|
||||
|
@ -51,22 +58,117 @@ public class PacketEntity implements PropertyHolder {
|
|||
return type;
|
||||
}
|
||||
|
||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
||||
public void setLocation(NpcLocation location) {
|
||||
this.location = location;
|
||||
for (Player viewer : viewers) packetFactory.teleportEntity(viewer, this);
|
||||
if (vehicle != null) {
|
||||
vehicle.setLocation(location.withY(location.getY() - 0.9));
|
||||
return;
|
||||
}
|
||||
for (Player viewer : viewable.getViewers()) packetFactory.teleportEntity(viewer, this);
|
||||
}
|
||||
|
||||
public void spawn(Player player) {
|
||||
if (type == EntityTypes.PLAYER) packetFactory.spawnPlayer(player, this, properties);
|
||||
else packetFactory.spawnEntity(player, this, properties);
|
||||
public CompletableFuture<Void> spawn(Player player) {
|
||||
return FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||
if (type == EntityTypes.PLAYER) packetFactory.spawnPlayer(player, this, properties).join();
|
||||
else packetFactory.spawnEntity(player, this, properties);
|
||||
if (vehicleId != null) {
|
||||
packetFactory.setPassengers(player, vehicleId, this.getEntityId());
|
||||
}
|
||||
if (passengers != null) {
|
||||
packetFactory.setPassengers(player, this.getEntityId(), passengers.stream().mapToInt(Integer::intValue).toArray());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setHeadRotation(Player player, float yaw, float pitch) {
|
||||
packetFactory.sendHeadRotation(player, this, yaw, pitch);
|
||||
}
|
||||
|
||||
public PacketEntity getVehicle() {
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
public Viewable getViewable() {
|
||||
return viewable;
|
||||
}
|
||||
|
||||
public void setVehicleId(Integer vehicleId) {
|
||||
if (this.vehicle != null) {
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.vehicle.getEntityId());
|
||||
this.vehicle.despawn(player);
|
||||
packetFactory.teleportEntity(player, this);
|
||||
}
|
||||
} else if (this.vehicleId != null) {
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.vehicleId);
|
||||
}
|
||||
}
|
||||
this.vehicleId = vehicleId;
|
||||
if (vehicleId == null) return;
|
||||
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.getEntityId(), vehicleId);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVehicle(PacketEntity vehicle) {
|
||||
// remove old vehicle
|
||||
if (this.vehicle != null) {
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.vehicle.getEntityId());
|
||||
this.vehicle.despawn(player);
|
||||
packetFactory.teleportEntity(player, this);
|
||||
}
|
||||
} else if (this.vehicleId != null) {
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.vehicleId);
|
||||
}
|
||||
}
|
||||
|
||||
this.vehicle = vehicle;
|
||||
if (this.vehicle == null) return;
|
||||
|
||||
vehicle.setLocation(location.withY(location.getY() - 0.9));
|
||||
for (Player player : viewable.getViewers()) {
|
||||
vehicle.spawn(player).thenRun(() -> {
|
||||
packetFactory.setPassengers(player, vehicle.getEntityId(), this.getEntityId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getVehicleId() {
|
||||
return vehicleId;
|
||||
}
|
||||
|
||||
public List<Integer> getPassengers() {
|
||||
return passengers == null ? Collections.emptyList() : passengers;
|
||||
}
|
||||
|
||||
public void addPassenger(int entityId) {
|
||||
if (passengers == null) {
|
||||
passengers = new ArrayList<>();
|
||||
}
|
||||
passengers.add(entityId);
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.getEntityId(), passengers.stream().mapToInt(Integer::intValue).toArray());
|
||||
}
|
||||
}
|
||||
|
||||
public void removePassenger(int entityId) {
|
||||
if (passengers == null) return;
|
||||
passengers.remove(entityId);
|
||||
for (Player player : viewable.getViewers()) {
|
||||
packetFactory.setPassengers(player, this.getEntityId(), passengers.stream().mapToInt(Integer::intValue).toArray());
|
||||
}
|
||||
if (passengers.isEmpty()) {
|
||||
passengers = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void despawn(Player player) {
|
||||
packetFactory.destroyEntity(player, this, properties);
|
||||
if (vehicle != null) vehicle.despawn(player);
|
||||
}
|
||||
|
||||
public void refreshMeta(Player player) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package lol.pyr.znpcsplus.entity.properties;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import lol.pyr.znpcsplus.entity.ArmorStandVehicleProperties;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class EntitySittingProperty extends EntityPropertyImpl<Boolean> {
|
||||
private final PacketFactory packetFactory;
|
||||
private final EntityPropertyRegistryImpl propertyRegistry;
|
||||
|
||||
public EntitySittingProperty(PacketFactory packetFactory, EntityPropertyRegistryImpl propertyRegistry) {
|
||||
super("entity_sitting", false, Boolean.class);
|
||||
this.packetFactory = packetFactory;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
||||
boolean sitting = entity.getProperty(this);
|
||||
if (sitting) {
|
||||
if (entity.getVehicle() == null) {
|
||||
PacketEntity vehiclePacketEntity = new PacketEntity(packetFactory, new ArmorStandVehicleProperties(propertyRegistry),
|
||||
entity.getViewable(), EntityTypes.ARMOR_STAND, entity.getLocation().withY(entity.getLocation().getY() - 0.9));
|
||||
entity.setVehicle(vehiclePacketEntity);
|
||||
}
|
||||
} else if (entity.getVehicle() != null) {
|
||||
entity.setVehicle(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,10 @@ public class GlowProperty extends EntityPropertyImpl<NamedColor> {
|
|||
EntityData oldData = properties.get(0);
|
||||
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
|
||||
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (value == null ? 0 : 0x40))));
|
||||
if (isSpawned) packetFactory.removeTeam(player, entity);
|
||||
packetFactory.createTeam(player, entity, value);
|
||||
// the team is already created with the right glow color in the packet factory if the npc isnt spawned yet
|
||||
if (isSpawned) {
|
||||
packetFactory.removeTeam(player, entity);
|
||||
packetFactory.createTeam(player, entity, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package lol.pyr.znpcsplus.entity.properties;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class HealthProperty extends EntityPropertyImpl<Float> {
|
||||
private final int index;
|
||||
|
||||
public HealthProperty(int index) {
|
||||
super("health", 20f, Float.class);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
||||
float health = entity.getProperty(this);
|
||||
health = (float) Attributes.MAX_HEALTH.sanitizeValue(health);
|
||||
properties.put(index, new EntityData(index, EntityDataTypes.FLOAT, health));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package lol.pyr.znpcsplus.entity.properties.attributes;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attribute;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AttributeProperty extends EntityPropertyImpl<Double> {
|
||||
private final PacketFactory packetFactory;
|
||||
private final Attribute attribute;
|
||||
|
||||
public AttributeProperty(PacketFactory packetFactory, String name, Attribute attribute) {
|
||||
super(name, attribute.getDefaultValue(), Double.class);
|
||||
this.packetFactory = packetFactory;
|
||||
this.attribute = attribute;
|
||||
}
|
||||
|
||||
public double getMinValue() {
|
||||
return attribute.getMinValue();
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return attribute.getMaxValue();
|
||||
}
|
||||
|
||||
public double sanitizeValue(double value) {
|
||||
return attribute.sanitizeValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
|
||||
apply(player, packetEntity, isSpawned, Collections.emptyList());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
||||
}
|
||||
|
||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, List<WrapperPlayServerUpdateAttributes.Property> properties) {
|
||||
Double value = entity.getProperty(this);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value = attribute.sanitizeValue(value);
|
||||
if (isSpawned) {
|
||||
packetFactory.sendAttribute(player, entity, new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
|
||||
} else {
|
||||
properties.add(new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
public Attribute getAttribute() {
|
||||
return attribute;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import lol.pyr.znpcsplus.api.hologram.Hologram;
|
|||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import lol.pyr.znpcsplus.util.Viewable;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
@ -16,6 +17,8 @@ import org.bukkit.entity.Player;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class HologramImpl extends Viewable implements Hologram {
|
||||
private final ConfigManager configManager;
|
||||
|
@ -38,9 +41,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
}
|
||||
|
||||
public void addTextLineComponent(Component line) {
|
||||
HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
|
||||
HologramText newLine = new HologramText(this, propertyRegistry, packetFactory, null, line);
|
||||
lines.add(newLine);
|
||||
relocateLines(newLine);
|
||||
relocateLines();
|
||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||
}
|
||||
|
||||
|
@ -57,9 +60,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
}
|
||||
|
||||
public void addItemLinePEStack(ItemStack item) {
|
||||
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
|
||||
HologramItem newLine = new HologramItem(this, propertyRegistry, packetFactory, null, item);
|
||||
lines.add(newLine);
|
||||
relocateLines(newLine);
|
||||
relocateLines();
|
||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||
}
|
||||
|
||||
|
@ -98,10 +101,16 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
lines.clear();
|
||||
}
|
||||
|
||||
public void addLines(List<HologramLine<?>> lines) {
|
||||
this.lines.addAll(lines);
|
||||
relocateLines();
|
||||
for (Player viewer : getViewers()) for (HologramLine<?> line : lines) line.show(viewer);
|
||||
}
|
||||
|
||||
public void insertTextLineComponent(int index, Component line) {
|
||||
HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
|
||||
HologramText newLine = new HologramText(this, propertyRegistry, packetFactory, null, line);
|
||||
lines.add(index, newLine);
|
||||
relocateLines(newLine);
|
||||
relocateLines();
|
||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||
}
|
||||
|
||||
|
@ -114,9 +123,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
}
|
||||
|
||||
public void insertItemLinePEStack(int index, ItemStack item) {
|
||||
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
|
||||
HologramItem newLine = new HologramItem(this, propertyRegistry, packetFactory, null, item);
|
||||
lines.add(index, newLine);
|
||||
relocateLines(newLine);
|
||||
relocateLines();
|
||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||
}
|
||||
|
||||
|
@ -138,8 +147,10 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void UNSAFE_show(Player player) {
|
||||
for (HologramLine<?> line : lines) line.show(player);
|
||||
protected CompletableFuture<Void> UNSAFE_show(Player player) {
|
||||
return FutureUtil.allOf(lines.stream()
|
||||
.map(line -> line.show(player))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,14 +183,10 @@ public class HologramImpl extends Viewable implements Hologram {
|
|||
}
|
||||
|
||||
private void relocateLines() {
|
||||
relocateLines(null);
|
||||
}
|
||||
|
||||
private void relocateLines(HologramLine<?> newLine) {
|
||||
final double lineSpacing = configManager.getConfig().lineSpacing();
|
||||
double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset();
|
||||
for (HologramLine<?> line : lines) {
|
||||
line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers());
|
||||
line.setLocation(location.withY(height));
|
||||
height -= lineSpacing;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,11 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
|||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import lol.pyr.znpcsplus.util.Viewable;
|
||||
|
||||
public class HologramItem extends HologramLine<ItemStack> {
|
||||
public HologramItem(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
|
||||
super(item, packetFactory, EntityTypes.ITEM, location);
|
||||
public HologramItem(Viewable viewable, EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
|
||||
super(viewable, item, packetFactory, EntityTypes.ITEM, location);
|
||||
addProperty(propertyRegistry.getByName("holo_item"));
|
||||
}
|
||||
|
||||
|
@ -33,8 +31,8 @@ public class HologramItem extends HologramLine<ItemStack> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
||||
super.setLocation(location.withY(location.getY() + 2.05), viewers);
|
||||
public void setLocation(NpcLocation location) {
|
||||
super.setLocation(location.withY(location.getY() + 2.05));
|
||||
}
|
||||
|
||||
public static boolean ensureValidItemInput(String in) {
|
||||
|
|
|
@ -7,21 +7,22 @@ import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
|||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import lol.pyr.znpcsplus.util.Viewable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class HologramLine<M> implements PropertyHolder {
|
||||
private M value;
|
||||
private final PacketEntity entity;
|
||||
private final Set<EntityProperty<?>> properties;
|
||||
|
||||
public HologramLine(M value, PacketFactory packetFactory, EntityType type, NpcLocation location) {
|
||||
public HologramLine(Viewable viewable, M value, PacketFactory packetFactory, EntityType type, NpcLocation location) {
|
||||
this.value = value;
|
||||
this.entity = new PacketEntity(packetFactory, this, type, location);
|
||||
this.entity = new PacketEntity(packetFactory, this, viewable, type, location);
|
||||
this.properties = new HashSet<>();
|
||||
}
|
||||
|
||||
|
@ -37,16 +38,16 @@ public class HologramLine<M> implements PropertyHolder {
|
|||
entity.refreshMeta(player);
|
||||
}
|
||||
|
||||
protected void show(Player player) {
|
||||
entity.spawn(player);
|
||||
protected CompletableFuture<Void> show(Player player) {
|
||||
return entity.spawn(player);
|
||||
}
|
||||
|
||||
protected void hide(Player player) {
|
||||
entity.despawn(player);
|
||||
}
|
||||
|
||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
||||
entity.setLocation(location, viewers);
|
||||
public void setLocation(NpcLocation location) {
|
||||
entity.setLocation(location);
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
|
|
|
@ -5,24 +5,26 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
|||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import lol.pyr.znpcsplus.util.Viewable;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class HologramText extends HologramLine<Component> {
|
||||
|
||||
private static final Component BLANK = Component.text("%blank%");
|
||||
|
||||
public HologramText(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
|
||||
super(text, packetFactory, EntityTypes.ARMOR_STAND, location);
|
||||
public HologramText(Viewable viewable, EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
|
||||
super(viewable, text, packetFactory, EntityTypes.ARMOR_STAND, location);
|
||||
addProperty(propertyRegistry.getByName("name"));
|
||||
addProperty(propertyRegistry.getByName("invisible"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(Player player) {
|
||||
if (!getValue().equals(BLANK)) {
|
||||
super.show(player);
|
||||
}
|
||||
public CompletableFuture<Void> show(Player player) {
|
||||
if (getValue().equals(BLANK)) return CompletableFuture.completedFuture(null);
|
||||
return super.show(player);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -37,4 +39,12 @@ public class HologramText extends HologramLine<Component> {
|
|||
public boolean hasProperty(EntityProperty<?> key) {
|
||||
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
HologramText that = (HologramText) obj;
|
||||
return getValue().equals(that.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class SwitchServerAction extends InteractionActionImpl {
|
|||
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||
Component.text("Click to edit this action", NamedTextColor.GRAY)))
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
||||
"/" + context.getLabel() + " action edit " + id + " " + index + " switcserver " + getInteractionType().name() + " " + getCooldown()/1000 + " " + getDelay() + " " + server))
|
||||
"/" + context.getLabel() + " action edit " + id + " " + index + " switchserver " + getInteractionType().name() + " " + getCooldown()/1000 + " " + getDelay() + " " + server))
|
||||
.append(Component.text(" | ", NamedTextColor.GRAY))
|
||||
.append(Component.text("[DELETE]", NamedTextColor.RED)
|
||||
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||
|
|
|
@ -20,9 +20,12 @@ import org.bukkit.Location;
|
|||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NpcImpl extends Viewable implements Npc {
|
||||
|
@ -38,6 +41,8 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
|
||||
private final List<InteractionAction> actions = new ArrayList<>();
|
||||
|
||||
private final Map<UUID, float[]> playerLookMap = new ConcurrentHashMap<>();
|
||||
|
||||
protected NpcImpl(UUID uuid, EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) {
|
||||
this(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world.getName(), type, location);
|
||||
}
|
||||
|
@ -48,14 +53,14 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
this.type = type;
|
||||
this.location = location;
|
||||
this.uuid = uuid;
|
||||
entity = new PacketEntity(packetFactory, this, type.getType(), location);
|
||||
entity = new PacketEntity(packetFactory, this, this, type.getType(), location);
|
||||
hologram = new HologramImpl(propertyRegistry, configManager, packetFactory, textSerializer, location.withY(location.getY() + type.getHologramOffset()));
|
||||
}
|
||||
|
||||
public void setType(NpcTypeImpl type) {
|
||||
UNSAFE_hideAll();
|
||||
this.type = type;
|
||||
entity = new PacketEntity(packetFactory, this, type.getType(), entity.getLocation());
|
||||
entity = new PacketEntity(packetFactory, this, this, type.getType(), entity.getLocation());
|
||||
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset()));
|
||||
UNSAFE_showAll();
|
||||
}
|
||||
|
@ -85,20 +90,34 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
|
||||
public void setLocation(NpcLocation location) {
|
||||
this.location = location;
|
||||
entity.setLocation(location, getViewers());
|
||||
playerLookMap.clear();
|
||||
playerLookMap.putAll(getViewers().stream().collect(Collectors.toMap(Player::getUniqueId, player -> new float[]{location.getYaw(), location.getPitch()})));
|
||||
entity.setLocation(location);
|
||||
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset()));
|
||||
}
|
||||
|
||||
public void setHeadRotation(Player player, float yaw, float pitch) {
|
||||
if (getHeadYaw(player) == yaw && getHeadPitch(player) == pitch) return;
|
||||
playerLookMap.put(player.getUniqueId(), new float[]{yaw, pitch});
|
||||
entity.setHeadRotation(player, yaw, pitch);
|
||||
}
|
||||
|
||||
public void setHeadRotation(float yaw, float pitch) {
|
||||
for (Player player : getViewers()) {
|
||||
if (getHeadYaw(player) == yaw && getHeadPitch(player) == pitch) continue;
|
||||
playerLookMap.put(player.getUniqueId(), new float[]{yaw, pitch});
|
||||
entity.setHeadRotation(player, yaw, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
public float getHeadYaw(Player player) {
|
||||
return playerLookMap.getOrDefault(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()})[0];
|
||||
}
|
||||
|
||||
public float getHeadPitch(Player player) {
|
||||
return playerLookMap.getOrDefault(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()})[1];
|
||||
}
|
||||
|
||||
public HologramImpl getHologram() {
|
||||
return hologram;
|
||||
}
|
||||
|
@ -125,13 +144,14 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void UNSAFE_show(Player player) {
|
||||
entity.spawn(player);
|
||||
hologram.show(player);
|
||||
protected CompletableFuture<Void> UNSAFE_show(Player player) {
|
||||
playerLookMap.put(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()});
|
||||
return CompletableFuture.allOf(entity.spawn(player), hologram.show(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void UNSAFE_hide(Player player) {
|
||||
playerLookMap.remove(player.getUniqueId());
|
||||
entity.despawn(player);
|
||||
hologram.hide(player);
|
||||
}
|
||||
|
@ -246,4 +266,29 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
public void swingHand(boolean offHand) {
|
||||
for (Player viewer : getViewers()) entity.swingHand(viewer, offHand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<Integer> getPassengers() {
|
||||
return entity.getPassengers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPassenger(int entityId) {
|
||||
entity.addPassenger(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePassenger(int entityId) {
|
||||
entity.removePassenger(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer getVehicleId() {
|
||||
return entity.getVehicleId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVehicleId(Integer vehicleId) {
|
||||
entity.setVehicleId(vehicleId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import lol.pyr.znpcsplus.hologram.HologramText;
|
|||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
|
@ -35,13 +36,13 @@ public class NpcRegistryImpl implements NpcRegistry {
|
|||
private final Map<String, NpcEntryImpl> npcIdLookupMap = new HashMap<>();
|
||||
private final Map<UUID, NpcEntryImpl> npcUuidLookupMap = new HashMap<>();
|
||||
|
||||
public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, NpcSerializerRegistryImpl serializerRegistry, LegacyComponentSerializer textSerializer) {
|
||||
this.textSerializer = textSerializer;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
|
||||
if (storage == null) {
|
||||
Bukkit.getLogger().warning("Failed to initialize storage, falling back to YAML");
|
||||
storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
|
||||
}
|
||||
this.packetFactory = packetFactory;
|
||||
this.configManager = configManager;
|
||||
|
@ -52,7 +53,13 @@ public class NpcRegistryImpl implements NpcRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(NpcEntry entry) {
|
||||
register((NpcEntryImpl) entry);
|
||||
}
|
||||
|
||||
private void register(NpcEntryImpl entry) {
|
||||
if (entry == null) throw new NullPointerException();
|
||||
unregister(npcIdLookupMap.put(entry.getId(), entry));
|
||||
unregister(npcUuidLookupMap.put(entry.getNpc().getUuid(), entry));
|
||||
npcList.add(entry);
|
||||
|
|
|
@ -114,12 +114,15 @@ public class NpcTypeImpl implements NpcType {
|
|||
|
||||
public NpcTypeImpl build() {
|
||||
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance",
|
||||
addProperties("fire", "invisible", "silent", "look", "look_distance", "look_return", "view_distance",
|
||||
"potion_color", "potion_ambient", "display_name", "permission_required",
|
||||
"player_knockback", "player_knockback_exempt_permission", "player_knockback_distance", "player_knockback_vertical",
|
||||
"player_knockback_horizontal", "player_knockback_cooldown", "player_knockback_sound", "player_knockback_sound_name",
|
||||
"player_knockback_sound_volume", "player_knockback_sound_pitch");
|
||||
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
|
||||
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) {
|
||||
addProperties("health", "attribute_max_health");
|
||||
}
|
||||
// TODO: make this look nicer after completing the rest of the properties
|
||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow");
|
||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) {
|
||||
|
@ -143,6 +146,9 @@ public class NpcTypeImpl implements NpcType {
|
|||
} else if (version.isOlderThan(ServerVersion.V_1_11) && type.equals(EntityTypes.HORSE)) {
|
||||
addProperties("has_chest");
|
||||
}
|
||||
if (version.isOlderThan(ServerVersion.V_1_11) && EntityTypes.isTypeInstanceOf(type, EntityTypes.SKELETON)) {
|
||||
addProperties("skeleton_type");
|
||||
}
|
||||
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_EVO_ILLU_ILLAGER)) {
|
||||
addProperties("spell");
|
||||
}
|
||||
|
@ -176,6 +182,11 @@ public class NpcTypeImpl implements NpcType {
|
|||
addProperties("wolf_variant");
|
||||
}
|
||||
}
|
||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_21_4)) {
|
||||
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.CREAKING)) {
|
||||
addProperties("creaking_crumbling");
|
||||
}
|
||||
}
|
||||
return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties), defaultProperties);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
register(builder(p, "player", EntityTypes.PLAYER)
|
||||
.setHologramOffset(-0.15D)
|
||||
.addEquipmentProperties()
|
||||
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right", "force_body_rotation")
|
||||
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right", "force_body_rotation", "entity_sitting")
|
||||
.addDefaultProperty("skin_cape", true)
|
||||
.addDefaultProperty("skin_jacket", true)
|
||||
.addDefaultProperty("skin_left_sleeve", true)
|
||||
|
@ -82,7 +82,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "enderman", EntityTypes.ENDERMAN)
|
||||
.setHologramOffset(0.925)
|
||||
.addProperties("enderman_held_block", "enderman_screaming", "enderman_staring"));
|
||||
.addProperties("enderman_held_block", "enderman_screaming", "enderman_staring", "entity_sitting"));
|
||||
|
||||
register(builder(p, "endermite", EntityTypes.ENDERMITE)
|
||||
.setHologramOffset(-1.675));
|
||||
|
@ -93,7 +93,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "giant", EntityTypes.GIANT)
|
||||
.setHologramOffset(10.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "guardian", EntityTypes.GUARDIAN)
|
||||
.setHologramOffset(-1.125)
|
||||
|
@ -133,7 +134,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "skeleton", EntityTypes.SKELETON)
|
||||
.setHologramOffset(0.015)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "skeleton_horse", EntityTypes.SKELETON_HORSE)
|
||||
.setHologramOffset(-0.375));
|
||||
|
@ -169,14 +171,16 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "zombie", EntityTypes.ZOMBIE)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "zombie_horse", EntityTypes.ZOMBIE_HORSE)
|
||||
.setHologramOffset(-0.375));
|
||||
|
||||
register(builder(p, "zombified_piglin", EntityTypes.ZOMBIFIED_PIGLIN)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
||||
|
||||
|
@ -203,21 +207,21 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "husk", EntityTypes.HUSK)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
|
||||
register(builder(p, "polar_bear", EntityTypes.POLAR_BEAR)
|
||||
.setHologramOffset(-0.575));
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "stray", EntityTypes.STRAY)
|
||||
.setHologramOffset(0.015)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "evoker", EntityTypes.EVOKER)
|
||||
.setHologramOffset(-0.025));
|
||||
.setHologramOffset(-0.025)
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "llama", EntityTypes.LLAMA)
|
||||
.setHologramOffset(-0.105)
|
||||
.addProperties("carpet_color", "llama_variant"));
|
||||
.addProperties("carpet_color", "llama_variant", "body"));
|
||||
|
||||
register(builder(p, "vex", EntityTypes.VEX)
|
||||
.setHologramOffset(-1.175)
|
||||
|
@ -225,20 +229,23 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "vindicator", EntityTypes.VINDICATOR)
|
||||
.setHologramOffset(-0.025)
|
||||
.addProperties("celebrating"));
|
||||
.addProperties("celebrating", "entity_sitting"));
|
||||
|
||||
register(builder(p, "wither_skeleton", EntityTypes.WITHER_SKELETON)
|
||||
.setHologramOffset(0.425)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "zombie_villager", EntityTypes.ZOMBIE_VILLAGER)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
|
||||
|
||||
register(builder(p, "illusioner", EntityTypes.ILLUSIONER)
|
||||
.setHologramOffset(-0.025));
|
||||
.setHologramOffset(-0.025)
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "parrot", EntityTypes.PARROT)
|
||||
.setHologramOffset(-1.075)
|
||||
|
@ -255,7 +262,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "drowned", EntityTypes.DROWNED)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "phantom", EntityTypes.PHANTOM)
|
||||
.setHologramOffset(-1.475));
|
||||
|
@ -291,7 +299,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
register(builder(p, "pillager", EntityTypes.PILLAGER)
|
||||
.setHologramOffset(-0.025)
|
||||
.addHandProperties()
|
||||
.addProperties("pillager_charging"));
|
||||
.addProperties("pillager_charging", "entity_sitting"));
|
||||
|
||||
register(builder(p, "ravager", EntityTypes.RAVAGER)
|
||||
.setHologramOffset(0.225));
|
||||
|
@ -319,11 +327,12 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
register(builder(p, "piglin", EntityTypes.PIGLIN)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties()
|
||||
.addProperties("piglin_baby", "piglin_charging_crossbow", "piglin_dancing"));
|
||||
.addProperties("piglin_baby", "piglin_charging_crossbow", "piglin_dancing", "entity_sitting"));
|
||||
|
||||
register(builder(p, "piglin_brute", EntityTypes.PIGLIN_BRUTE)
|
||||
.setHologramOffset(-0.025)
|
||||
.addEquipmentProperties());
|
||||
.addEquipmentProperties()
|
||||
.addProperties("entity_sitting"));
|
||||
|
||||
register(builder(p, "strider", EntityTypes.STRIDER)
|
||||
.setHologramOffset(-0.275)
|
||||
|
@ -372,6 +381,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
.setHologramOffset(0.4)
|
||||
.addProperties("bashing", "camel_sitting"));
|
||||
|
||||
|
||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return;
|
||||
|
||||
register(builder(p, "armadillo", EntityTypes.ARMADILLO)
|
||||
|
@ -382,10 +392,16 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
|
||||
register(builder(p, "bogged", EntityTypes.BOGGED)
|
||||
.setHologramOffset(0.015)
|
||||
.addProperties("bogged_sheared"));
|
||||
.addProperties("bogged_sheared", "entity_sitting"));
|
||||
|
||||
register(builder(p, "breeze", EntityTypes.BREEZE)
|
||||
.setHologramOffset(-0.205));
|
||||
|
||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_21_2)) return;
|
||||
|
||||
register(builder(p, "creaking", EntityTypes.CREAKING)
|
||||
.setHologramOffset(0.725)
|
||||
.addProperties("creaking_active"));
|
||||
}
|
||||
|
||||
public Collection<NpcType> getAll() {
|
||||
|
|
|
@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.packets;
|
|||
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.protocol.player.Equipment;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.util.NamedColor;
|
||||
|
@ -11,7 +12,7 @@ import java.util.List;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface PacketFactory {
|
||||
void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
CompletableFuture<Void> spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void spawnEntity(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void destroyEntity(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void teleportEntity(Player player, PacketEntity entity);
|
||||
|
@ -24,4 +25,7 @@ public interface PacketFactory {
|
|||
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
|
||||
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
|
||||
void sendHandSwing(Player player, PacketEntity entity, boolean offHand);
|
||||
void setPassengers(Player player, int vehicle, int... passengers);
|
||||
void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lol.pyr.znpcsplus.packets;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
|
||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
|
@ -27,6 +28,7 @@ public class V1_17PacketFactory extends V1_8PacketFactory {
|
|||
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
|
||||
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
|
||||
sendAllMetadata(player, entity, properties);
|
||||
if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
|
||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
|
||||
|
||||
|
@ -27,14 +28,15 @@ public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||
public CompletableFuture<Void> spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
return addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||
NpcLocation location = entity.getLocation();
|
||||
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
|
||||
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
|
||||
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
||||
sendAllMetadata(player, entity, properties);
|
||||
sendAllAttributes(player, entity, properties);
|
||||
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import lol.pyr.znpcsplus.config.ConfigManager;
|
|||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
|
||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
|
||||
import lol.pyr.znpcsplus.util.NamedColor;
|
||||
|
@ -47,14 +48,15 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||
public CompletableFuture<Void> spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
return addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||
NpcLocation location = entity.getLocation();
|
||||
sendPacket(player, new WrapperPlayServerSpawnPlayer(entity.getEntityId(),
|
||||
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
|
||||
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
||||
sendAllMetadata(player, entity, properties);
|
||||
sendAllAttributes(player, entity, properties);
|
||||
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
|
||||
});
|
||||
}
|
||||
|
@ -70,6 +72,7 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location),
|
||||
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
|
||||
sendAllMetadata(player, entity, properties);
|
||||
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
|
||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||
}
|
||||
|
||||
|
@ -153,6 +156,11 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassengers(Player player, int vehicleEntityId, int... passengers) {
|
||||
sendPacket(player, new WrapperPlayServerSetPassengers(vehicleEntityId, passengers));
|
||||
}
|
||||
|
||||
protected void sendPacket(Player player, PacketWrapper<?> packet) {
|
||||
packetEvents.getPlayerManager().sendPacket(player, packet);
|
||||
}
|
||||
|
@ -182,4 +190,19 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_OFF_HAND :
|
||||
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_MAIN_ARM));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
List<WrapperPlayServerUpdateAttributes.Property> attributesList = new ArrayList<>();
|
||||
properties.getAppliedProperties()
|
||||
.stream()
|
||||
.filter(property -> property instanceof AttributeProperty)
|
||||
.forEach(property -> ((AttributeProperty) property).apply(player, entity, false, attributesList));
|
||||
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), attributesList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property) {
|
||||
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), Collections.singletonList(property)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package lol.pyr.znpcsplus.serialization;
|
||||
|
||||
import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
|
||||
import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NpcSerializerRegistryImpl implements NpcSerializerRegistry {
|
||||
private final Map<Class<?>, NpcSerializer<?>> serializerMap = new HashMap<>();
|
||||
|
||||
public NpcSerializerRegistryImpl(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
registerSerializer(YamlConfiguration.class, new YamlSerializer(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> NpcSerializer<T> getSerializer(Class<T> clazz) {
|
||||
return (NpcSerializer<T>) serializerMap.get(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void registerSerializer(Class<T> clazz, NpcSerializer<T> serializer) {
|
||||
serializerMap.put(clazz, serializer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package lol.pyr.znpcsplus.serialization;
|
||||
|
||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||
import lol.pyr.znpcsplus.api.npc.NpcEntry;
|
||||
import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PropertySerializer;
|
||||
import lol.pyr.znpcsplus.hologram.HologramImpl;
|
||||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class YamlSerializer implements NpcSerializer<YamlConfiguration> {
|
||||
private final static Logger logger = Logger.getLogger("YamlSerializer");
|
||||
|
||||
private final PacketFactory packetFactory;
|
||||
private final ConfigManager configManager;
|
||||
private final ActionRegistryImpl actionRegistry;
|
||||
private final NpcTypeRegistryImpl typeRegistry;
|
||||
private final EntityPropertyRegistryImpl propertyRegistry;
|
||||
private final LegacyComponentSerializer textSerializer;
|
||||
|
||||
public YamlSerializer(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
this.packetFactory = packetFactory;
|
||||
this.configManager = configManager;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.typeRegistry = typeRegistry;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
this.textSerializer = textSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public YamlConfiguration serialize(NpcEntry entry) {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("id", entry.getId());
|
||||
config.set("is-processed", entry.isProcessed());
|
||||
config.set("allow-commands", entry.isAllowCommandModification());
|
||||
config.set("save", entry.isSave());
|
||||
|
||||
NpcImpl npc = (NpcImpl) entry.getNpc();
|
||||
config.set("enabled", npc.isEnabled());
|
||||
config.set("uuid", npc.getUuid().toString());
|
||||
config.set("world", npc.getWorldName());
|
||||
config.set("location", serializeLocation(npc.getLocation()));
|
||||
config.set("type", npc.getType().getName());
|
||||
|
||||
for (EntityProperty<?> property : npc.getAllProperties()) try {
|
||||
PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType());
|
||||
if (serializer == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + property.getName() + "' for npc '" + entry.getId() + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property)));
|
||||
} catch (Exception exception) {
|
||||
logger.severe("Failed to serialize property " + property.getName() + " for npc with id " + entry.getId());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
HologramImpl hologram = npc.getHologram();
|
||||
if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset());
|
||||
if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay());
|
||||
List<String> lines = new ArrayList<>(npc.getHologram().getLines().size());
|
||||
for (int i = 0; i < hologram.getLines().size(); i++) {
|
||||
lines.add(hologram.getLine(i));
|
||||
}
|
||||
config.set("hologram.lines", lines);
|
||||
config.set("actions", npc.getActions().stream()
|
||||
.map(actionRegistry::serialize)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpcEntry deserialize(YamlConfiguration config) {
|
||||
UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
|
||||
NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, config.getString("world"),
|
||||
typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location")));
|
||||
|
||||
if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled"));
|
||||
|
||||
ConfigurationSection properties = config.getConfigurationSection("properties");
|
||||
if (properties != null) {
|
||||
for (String key : properties.getKeys(false)) {
|
||||
EntityPropertyImpl<?> property = propertyRegistry.getByName(key);
|
||||
if (property == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
PropertySerializer<?> serializer = propertyRegistry.getSerializer(property.getType());
|
||||
if (serializer == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
Object value = serializer.deserialize(properties.getString(key));
|
||||
if (value == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to deserialize property '" + key + "' for npc '" + config.getString("id") + "'. Resetting to default ...");
|
||||
value = property.getDefaultValue();
|
||||
}
|
||||
npc.UNSAFE_setProperty(property, value);
|
||||
}
|
||||
}
|
||||
HologramImpl hologram = npc.getHologram();
|
||||
hologram.setOffset(config.getDouble("hologram.offset", 0.0));
|
||||
hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1));
|
||||
for (String line : config.getStringList("hologram.lines")) hologram.addLine(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"));
|
||||
entry.setAllowCommandModification(config.getBoolean("allow-commands"));
|
||||
entry.setSave(config.getBoolean("save", true));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public NpcLocation deserializeLocation(ConfigurationSection section) {
|
||||
return new NpcLocation(
|
||||
section.getDouble("x"),
|
||||
section.getDouble("y"),
|
||||
section.getDouble("z"),
|
||||
(float) section.getDouble("yaw"),
|
||||
(float) section.getDouble("pitch")
|
||||
);
|
||||
}
|
||||
|
||||
public YamlConfiguration serializeLocation(NpcLocation location) {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("x", location.getX());
|
||||
config.set("y", location.getY());
|
||||
config.set("z", location.getZ());
|
||||
config.set("yaw", location.getYaw());
|
||||
config.set("pitch", location.getPitch());
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -52,4 +52,9 @@ public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
|
|||
public SkinDescriptor createUrlDescriptor(URL url, String variant) {
|
||||
return PrefetchedDescriptor.fromUrl(skinCache, url, variant).join();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkinDescriptor createFileDescriptor(String path) {
|
||||
return PrefetchedDescriptor.fromFile(skinCache, path).join();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.google.gson.JsonParser;
|
|||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.reflection.Reflections;
|
||||
import lol.pyr.znpcsplus.skin.SkinImpl;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
@ -14,6 +15,7 @@ import java.net.HttpURLConnection;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -26,9 +28,12 @@ public class MojangSkinCache {
|
|||
|
||||
private final Map<String, SkinImpl> cache = new ConcurrentHashMap<>();
|
||||
private final Map<String, CachedId> idCache = new ConcurrentHashMap<>();
|
||||
private final File skinsFolder;
|
||||
|
||||
public MojangSkinCache(ConfigManager configManager) {
|
||||
public MojangSkinCache(ConfigManager configManager, File skinsFolder) {
|
||||
this.configManager = configManager;
|
||||
this.skinsFolder = skinsFolder;
|
||||
if (!skinsFolder.exists()) skinsFolder.mkdirs();
|
||||
}
|
||||
|
||||
public void cleanCache() {
|
||||
|
@ -42,7 +47,7 @@ public class MojangSkinCache {
|
|||
|
||||
if (idCache.containsKey(name.toLowerCase())) return fetchByUUID(idCache.get(name.toLowerCase()).getId());
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> {
|
||||
URL url = parseUrl("https://api.mojang.com/users/profiles/minecraft/" + name);
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
|
@ -75,7 +80,7 @@ public class MojangSkinCache {
|
|||
|
||||
if (idCache.containsKey(name.toLowerCase())) return fetchByUUID(idCache.get(name.toLowerCase()).getId());
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> {
|
||||
URL url = parseUrl("https://api.ashcon.app/mojang/v2/user/" + name);
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
|
@ -106,7 +111,7 @@ public class MojangSkinCache {
|
|||
}
|
||||
|
||||
public CompletableFuture<SkinImpl> fetchByUrl(URL url, String variant) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> {
|
||||
URL apiUrl = parseUrl("https://api.mineskin.org/generate/url");
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
|
@ -142,6 +147,58 @@ public class MojangSkinCache {
|
|||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<SkinImpl> fetchFromFile(String path) throws FileNotFoundException {
|
||||
File file = new File(skinsFolder, path);
|
||||
if (!file.exists()) throw new FileNotFoundException("File not found: " + path);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
URL apiUrl = parseUrl("https://api.mineskin.org/generate/upload");
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
String boundary = "*****";
|
||||
String CRLF = "\r\n";
|
||||
|
||||
connection = (HttpURLConnection) apiUrl.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setReadTimeout(10000);
|
||||
connection.setConnectTimeout(15000);
|
||||
connection.setUseCaches(false);
|
||||
connection.setRequestProperty("Cache-Control", "no-cache");
|
||||
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(outputStream);
|
||||
out.writeBytes("--" + boundary + CRLF);
|
||||
out.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"" + CRLF);
|
||||
out.writeBytes("Content-Type: image/png" + CRLF);
|
||||
out.writeBytes(CRLF);
|
||||
out.write(Files.readAllBytes(file.toPath()));
|
||||
out.writeBytes(CRLF);
|
||||
out.writeBytes("--" + boundary + "--" + CRLF);
|
||||
out.flush();
|
||||
out.close();
|
||||
outputStream.close();
|
||||
|
||||
try (Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) {
|
||||
JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject();
|
||||
if (obj.has("error")) return null;
|
||||
if (!obj.has("data")) return null;
|
||||
JsonObject texture = obj.get("data").getAsJsonObject().get("texture").getAsJsonObject();
|
||||
return new SkinImpl(texture.get("value").getAsString(), texture.get("signature").getAsString());
|
||||
}
|
||||
|
||||
} catch (IOException exception) {
|
||||
if (!configManager.getConfig().disableSkinFetcherWarnings()) {
|
||||
logger.warning("Failed to get skin from file:");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
if (connection != null) connection.disconnect();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isNameFullyCached(String s) {
|
||||
String name = s.toLowerCase();
|
||||
if (!idCache.containsKey(name)) return false;
|
||||
|
@ -170,7 +227,7 @@ public class MojangSkinCache {
|
|||
if (!skin.isExpired()) return CompletableFuture.completedFuture(skin);
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> {
|
||||
URL url = parseUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false");
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
|
@ -213,4 +270,8 @@ public class MojangSkinCache {
|
|||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public File getSkinsFolder() {
|
||||
return skinsFolder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
|||
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
|
||||
import lol.pyr.znpcsplus.skin.SkinImpl;
|
||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
|
@ -18,11 +20,21 @@ public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor
|
|||
}
|
||||
|
||||
public static CompletableFuture<PrefetchedDescriptor> forPlayer(MojangSkinCache cache, String name) {
|
||||
return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByName(name).join()));
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> new PrefetchedDescriptor(cache.fetchByName(name).join()));
|
||||
}
|
||||
|
||||
public static CompletableFuture<PrefetchedDescriptor> fromUrl(MojangSkinCache cache, URL url, String variant) {
|
||||
return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByUrl(url, variant).join()));
|
||||
return FutureUtil.exceptionPrintingSupplyAsync(() -> new PrefetchedDescriptor(cache.fetchByUrl(url, variant).join()));
|
||||
}
|
||||
|
||||
public static CompletableFuture<PrefetchedDescriptor> fromFile(MojangSkinCache cache, String path) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return new PrefetchedDescriptor(cache.fetchFromFile(path).join());
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
|||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.storage.mysql.MySQLStorage;
|
||||
import lol.pyr.znpcsplus.storage.sqlite.SQLiteStorage;
|
||||
import lol.pyr.znpcsplus.storage.yaml.YamlStorage;
|
||||
|
@ -16,13 +17,13 @@ import java.io.File;
|
|||
public enum NpcStorageType {
|
||||
YAML {
|
||||
@Override
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
return new YamlStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "data"));
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
|
||||
return new YamlStorage(serializerRegistry, new File(plugin.getDataFolder(), "data"));
|
||||
}
|
||||
},
|
||||
SQLITE {
|
||||
@Override
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
|
||||
try {
|
||||
return new SQLiteStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "znpcsplus.sqlite"));
|
||||
} catch (Exception e) {
|
||||
|
@ -33,7 +34,7 @@ public enum NpcStorageType {
|
|||
},
|
||||
MYSQL {
|
||||
@Override
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
|
||||
public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
|
||||
try {
|
||||
return new MySQLStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||
} catch (Exception e) {
|
||||
|
@ -43,5 +44,5 @@ public enum NpcStorageType {
|
|||
}
|
||||
};
|
||||
|
||||
public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer);
|
||||
public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry);
|
||||
}
|
||||
|
|
|
@ -1,47 +1,28 @@
|
|||
package lol.pyr.znpcsplus.storage.yaml;
|
||||
|
||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PropertySerializer;
|
||||
import lol.pyr.znpcsplus.hologram.HologramImpl;
|
||||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||
import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
|
||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcImpl;
|
||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class YamlStorage implements NpcStorage {
|
||||
private final static Logger logger = Logger.getLogger("YamlStorage");
|
||||
|
||||
private final PacketFactory packetFactory;
|
||||
private final ConfigManager configManager;
|
||||
private final ActionRegistryImpl actionRegistry;
|
||||
private final NpcTypeRegistryImpl typeRegistry;
|
||||
private final EntityPropertyRegistryImpl propertyRegistry;
|
||||
private final LegacyComponentSerializer textSerializer;
|
||||
private final File folder;
|
||||
private final NpcSerializer<YamlConfiguration> yamlSerializer;
|
||||
|
||||
public YamlStorage(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, File folder) {
|
||||
this.packetFactory = packetFactory;
|
||||
this.configManager = configManager;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.typeRegistry = typeRegistry;
|
||||
this.propertyRegistry = propertyRegistry;
|
||||
this.textSerializer = textSerializer;
|
||||
public YamlStorage(NpcSerializerRegistryImpl serializerRegistry, File folder) {
|
||||
this.yamlSerializer = serializerRegistry.getSerializer(YamlConfiguration.class);
|
||||
this.folder = folder;
|
||||
if (!this.folder.exists()) this.folder.mkdirs();
|
||||
}
|
||||
|
@ -53,45 +34,7 @@ public class YamlStorage implements NpcStorage {
|
|||
List<NpcEntryImpl> npcs = new ArrayList<>(files.length);
|
||||
for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) try {
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
|
||||
NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, config.getString("world"),
|
||||
typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location")));
|
||||
|
||||
if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled"));
|
||||
|
||||
ConfigurationSection properties = config.getConfigurationSection("properties");
|
||||
if (properties != null) {
|
||||
for (String key : properties.getKeys(false)) {
|
||||
EntityPropertyImpl<?> property = propertyRegistry.getByName(key);
|
||||
if (property == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
PropertySerializer<?> serializer = propertyRegistry.getSerializer(property.getType());
|
||||
if (serializer == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
Object value = serializer.deserialize(properties.getString(key));
|
||||
if (value == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to deserialize property '" + key + "' for npc '" + config.getString("id") + "'. Resetting to default ...");
|
||||
value = property.getDefaultValue();
|
||||
}
|
||||
npc.UNSAFE_setProperty(property, value);
|
||||
}
|
||||
}
|
||||
HologramImpl hologram = npc.getHologram();
|
||||
hologram.setOffset(config.getDouble("hologram.offset", 0.0));
|
||||
hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1));
|
||||
for (String line : config.getStringList("hologram.lines")) hologram.addLine(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"));
|
||||
entry.setAllowCommandModification(config.getBoolean("allow-commands"));
|
||||
entry.setSave(true);
|
||||
|
||||
npcs.add(entry);
|
||||
npcs.add((NpcEntryImpl) yamlSerializer.deserialize(config));
|
||||
} catch (Throwable t) {
|
||||
logger.severe("Failed to load npc file: " + file.getName());
|
||||
t.printStackTrace();
|
||||
|
@ -102,43 +45,7 @@ public class YamlStorage implements NpcStorage {
|
|||
@Override
|
||||
public void saveNpcs(Collection<NpcEntryImpl> npcs) {
|
||||
for (NpcEntryImpl entry : npcs) try {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("id", entry.getId());
|
||||
config.set("is-processed", entry.isProcessed());
|
||||
config.set("allow-commands", entry.isAllowCommandModification());
|
||||
|
||||
NpcImpl npc = entry.getNpc();
|
||||
config.set("enabled", npc.isEnabled());
|
||||
config.set("uuid", npc.getUuid().toString());
|
||||
config.set("world", npc.getWorldName());
|
||||
config.set("location", serializeLocation(npc.getLocation()));
|
||||
config.set("type", npc.getType().getName());
|
||||
|
||||
for (EntityProperty<?> property : npc.getAllProperties()) try {
|
||||
PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType());
|
||||
if (serializer == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + property.getName() + "' for npc '" + entry.getId() + "'. skipping ...");
|
||||
continue;
|
||||
}
|
||||
config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property)));
|
||||
} catch (Exception exception) {
|
||||
logger.severe("Failed to serialize property " + property.getName() + " for npc with id " + entry.getId());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
HologramImpl hologram = npc.getHologram();
|
||||
if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset());
|
||||
if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay());
|
||||
List<String> lines = new ArrayList<>(npc.getHologram().getLines().size());
|
||||
for (int i = 0; i < hologram.getLines().size(); i++) {
|
||||
lines.add(hologram.getLine(i));
|
||||
}
|
||||
config.set("hologram.lines", lines);
|
||||
config.set("actions", npc.getActions().stream()
|
||||
.map(actionRegistry::serialize)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
YamlConfiguration config = yamlSerializer.serialize(entry);
|
||||
config.save(fileFor(entry));
|
||||
} catch (Exception exception) {
|
||||
logger.severe("Failed to save npc with id " + entry.getId());
|
||||
|
|
|
@ -33,6 +33,7 @@ public class NpcProcessorTask extends BukkitRunnable {
|
|||
EntityPropertyImpl<Integer> viewDistanceProperty = propertyRegistry.getByName("view_distance", Integer.class); // Not sure why this is an Integer, but it is
|
||||
EntityPropertyImpl<LookType> lookProperty = propertyRegistry.getByName("look", LookType.class);
|
||||
EntityPropertyImpl<Double> lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.class);
|
||||
EntityPropertyImpl<Boolean> lookReturnProperty = propertyRegistry.getByName("look_return", Boolean.class);
|
||||
EntityPropertyImpl<Boolean> permissionRequiredProperty = propertyRegistry.getByName("permission_required", Boolean.class);
|
||||
EntityPropertyImpl<Boolean> playerKnockbackProperty = propertyRegistry.getByName("player_knockback", Boolean.class);
|
||||
EntityPropertyImpl<String> playerKnockbackExemptPermissionProperty = propertyRegistry.getByName("player_knockback_exempt_permission", String.class);
|
||||
|
@ -45,6 +46,7 @@ public class NpcProcessorTask extends BukkitRunnable {
|
|||
EntityPropertyImpl<Float> playerKnockbackSoundVolumeProperty = propertyRegistry.getByName("player_knockback_sound_volume", Float.class);
|
||||
EntityPropertyImpl<Float> playerKnockbackSoundPitchProperty = propertyRegistry.getByName("player_knockback_sound_pitch", Float.class);
|
||||
double lookDistance;
|
||||
boolean lookReturn;
|
||||
boolean permissionRequired;
|
||||
boolean playerKnockback;
|
||||
String playerKnockbackExemptPermission = null;
|
||||
|
@ -64,6 +66,7 @@ public class NpcProcessorTask extends BukkitRunnable {
|
|||
Player closest = null;
|
||||
LookType lookType = npc.getProperty(lookProperty);
|
||||
lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty));
|
||||
lookReturn = npc.getProperty(lookReturnProperty);
|
||||
permissionRequired = npc.getProperty(permissionRequiredProperty);
|
||||
playerKnockback = npc.getProperty(playerKnockbackProperty);
|
||||
if (playerKnockback) {
|
||||
|
@ -106,9 +109,13 @@ public class NpcProcessorTask extends BukkitRunnable {
|
|||
closestDist = distance;
|
||||
closest = player;
|
||||
}
|
||||
if (lookType.equals(LookType.PER_PLAYER) && lookDistance >= distance) {
|
||||
NpcLocation expected = npc.getLocation().lookingAt(player.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
||||
if (!expected.equals(npc.getLocation())) npc.setHeadRotation(player, expected.getYaw(), expected.getPitch());
|
||||
if (lookType.equals(LookType.PER_PLAYER)) {
|
||||
if (lookDistance >= distance) {
|
||||
NpcLocation expected = npc.getLocation().lookingAt(player.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
||||
npc.setHeadRotation(player, expected.getYaw(), expected.getPitch());
|
||||
} else if (lookReturn) {
|
||||
npc.setHeadRotation(player, npc.getLocation().getYaw(), npc.getLocation().getPitch());
|
||||
}
|
||||
}
|
||||
|
||||
// player knockback
|
||||
|
@ -132,7 +139,11 @@ public class NpcProcessorTask extends BukkitRunnable {
|
|||
if (closest != null && lookDistance >= closestDist) {
|
||||
NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
||||
if (!expected.equals(npc.getLocation())) npc.setHeadRotation(expected.getYaw(), expected.getPitch());
|
||||
} else if (lookReturn) {
|
||||
npc.setHeadRotation(npc.getLocation().getYaw(), npc.getLocation().getPitch());
|
||||
}
|
||||
} else if (lookType.equals(LookType.FIXED)) {
|
||||
npc.setHeadRotation(npc.getLocation().getYaw(), npc.getLocation().getPitch());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
34
plugin/src/main/java/lol/pyr/znpcsplus/util/FutureUtil.java
Normal file
34
plugin/src/main/java/lol/pyr/znpcsplus/util/FutureUtil.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
package lol.pyr.znpcsplus.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class FutureUtil {
|
||||
public static CompletableFuture<Void> allOf(Collection<CompletableFuture<?>> futures) {
|
||||
return exceptionPrintingRunAsync(() -> {
|
||||
for (CompletableFuture<?> future : futures) future.join();
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<T> newExceptionPrintingFuture() {
|
||||
return new CompletableFuture<T>().exceptionally(throwable -> {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> exceptionPrintingRunAsync(Runnable runnable) {
|
||||
return CompletableFuture.runAsync(runnable).exceptionally(throwable -> {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<T> exceptionPrintingSupplyAsync(Supplier<T> supplier) {
|
||||
return CompletableFuture.supplyAsync(supplier).exceptionally(throwable -> {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,11 +4,10 @@ import org.bukkit.entity.Player;
|
|||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Viewable {
|
||||
|
@ -21,38 +20,74 @@ public abstract class Viewable {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean queueRunning = false;
|
||||
private final Queue<Runnable> visibilityTaskQueue = new ConcurrentLinkedQueue<>();
|
||||
private final Set<Player> viewers = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public Viewable() {
|
||||
all.add(new WeakReference<>(this));
|
||||
}
|
||||
|
||||
private void tryRunQueue() {
|
||||
if (visibilityTaskQueue.isEmpty() || queueRunning) return;
|
||||
queueRunning = true;
|
||||
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||
while (!visibilityTaskQueue.isEmpty()) try {
|
||||
visibilityTaskQueue.remove().run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
queueRunning = false;
|
||||
});
|
||||
}
|
||||
|
||||
private void queueVisibilityTask(Runnable runnable) {
|
||||
visibilityTaskQueue.add(runnable);
|
||||
tryRunQueue();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
UNSAFE_hideAll();
|
||||
viewers.clear();
|
||||
queueVisibilityTask(() -> {
|
||||
UNSAFE_hideAll();
|
||||
viewers.clear();
|
||||
});
|
||||
}
|
||||
|
||||
public void respawn() {
|
||||
UNSAFE_hideAll();
|
||||
UNSAFE_showAll();
|
||||
public CompletableFuture<Void> respawn() {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
queueVisibilityTask(() -> {
|
||||
UNSAFE_hideAll();
|
||||
UNSAFE_showAll().join();
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void respawn(Player player) {
|
||||
if (!viewers.contains(player)) return;
|
||||
UNSAFE_hide(player);
|
||||
UNSAFE_show(player);
|
||||
public CompletableFuture<Void> respawn(Player player) {
|
||||
hide(player);
|
||||
return show(player);
|
||||
}
|
||||
|
||||
public void show(Player player) {
|
||||
if (viewers.contains(player)) return;
|
||||
viewers.add(player);
|
||||
UNSAFE_show(player);
|
||||
public CompletableFuture<Void> show(Player player) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
queueVisibilityTask(() -> {
|
||||
if (viewers.contains(player)) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
viewers.add(player);
|
||||
UNSAFE_show(player).join();
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void hide(Player player) {
|
||||
if (!viewers.contains(player)) return;
|
||||
viewers.remove(player);
|
||||
UNSAFE_hide(player);
|
||||
queueVisibilityTask(() -> {
|
||||
if (!viewers.contains(player)) return;
|
||||
viewers.remove(player);
|
||||
UNSAFE_hide(player);
|
||||
});
|
||||
}
|
||||
|
||||
public void UNSAFE_removeViewer(Player player) {
|
||||
|
@ -63,8 +98,10 @@ public abstract class Viewable {
|
|||
for (Player viewer : viewers) UNSAFE_hide(viewer);
|
||||
}
|
||||
|
||||
protected void UNSAFE_showAll() {
|
||||
for (Player viewer : viewers) UNSAFE_show(viewer);
|
||||
protected CompletableFuture<Void> UNSAFE_showAll() {
|
||||
return FutureUtil.allOf(viewers.stream()
|
||||
.map(this::UNSAFE_show)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public Set<Player> getViewers() {
|
||||
|
@ -75,7 +112,7 @@ public abstract class Viewable {
|
|||
return viewers.contains(player);
|
||||
}
|
||||
|
||||
protected abstract void UNSAFE_show(Player player);
|
||||
protected abstract CompletableFuture<Void> UNSAFE_show(Player player);
|
||||
|
||||
protected abstract void UNSAFE_hide(Player player);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ api-version: 1.13
|
|||
|
||||
folia-supported: true
|
||||
|
||||
depend:
|
||||
- packetevents
|
||||
|
||||
softdepend:
|
||||
- PlaceholderAPI
|
||||
- ServersNPC
|
||||
|
|
Loading…
Reference in a new issue