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
|
## Credits
|
||||||
- [PacketEvents 2.0](https://github.com/retrooper/packetevents) - Packet library
|
- [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
|
- [gson](https://github.com/google/gson) - JSON parsing library made by Google
|
||||||
- [Mineskin.org](https://mineskin.org/) - Website for raw skin file uploads
|
- [Mineskin.org](https://mineskin.org/) - Website for raw skin file uploads
|
||||||
- [adventure](https://docs.advntr.dev/) - Minecraft text api
|
- [adventure](https://docs.advntr.dev/) - Minecraft text api
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "maven-publish"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
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.interaction.ActionRegistry;
|
||||||
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
|
import lol.pyr.znpcsplus.api.npc.NpcRegistry;
|
||||||
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
|
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
|
||||||
|
import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry;
|
||||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
|
import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,4 +47,10 @@ public interface NpcApi {
|
||||||
* @return the skin descriptor factory
|
* @return the skin descriptor factory
|
||||||
*/
|
*/
|
||||||
SkinDescriptorFactory getSkinDescriptorFactory();
|
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 {
|
public interface ActionRegistry {
|
||||||
void register(InteractionActionType<?> type);
|
void register(InteractionActionType<?> type);
|
||||||
|
|
||||||
void unregister(Class<? extends InteractionAction> clazz);
|
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 lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all NPCs
|
* Base class for all NPCs
|
||||||
|
@ -135,14 +137,16 @@ public interface Npc extends PropertyHolder {
|
||||||
/**
|
/**
|
||||||
* Shows this NPC to a player
|
* Shows this NPC to a player
|
||||||
* @param player The {@link Player} to show to
|
* @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
|
* Respawns this NPC for a player
|
||||||
* @param player The {@link Player} to respawn for
|
* @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
|
* 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
|
* @param offHand Should the hand be the offhand
|
||||||
*/
|
*/
|
||||||
void swingHand(boolean 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
|
* @param id The ID of the NPC entry
|
||||||
*/
|
*/
|
||||||
void delete(String id);
|
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 createStaticDescriptor(String texture, String signature);
|
||||||
SkinDescriptor createUrlDescriptor(String url, String variant);
|
SkinDescriptor createUrlDescriptor(String url, String variant);
|
||||||
SkinDescriptor createUrlDescriptor(URL 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"
|
apply plugin: "java"
|
||||||
|
|
||||||
group "lol.pyr"
|
group "lol.pyr"
|
||||||
version "2.0.0" + (System.getenv().containsKey("BUILD_ID") ? "-SNAPSHOT" : "")
|
version "2.0.0-netherite-SNAPSHOT"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compileOnly "org.jetbrains:annotations:26.0.1"
|
||||||
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
|
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +21,9 @@ subprojects {
|
||||||
maven {
|
maven {
|
||||||
url "https://repo.codemc.io/repository/maven-releases/"
|
url "https://repo.codemc.io/repository/maven-releases/"
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
url "https://repo.codemc.io/repository/maven-snapshots/"
|
||||||
|
}
|
||||||
maven {
|
maven {
|
||||||
url "https://libraries.minecraft.net"
|
url "https://libraries.minecraft.net"
|
||||||
}
|
}
|
||||||
|
@ -32,5 +36,23 @@ subprojects {
|
||||||
maven {
|
maven {
|
||||||
url "https://repo.pyr.lol/releases"
|
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 {
|
javaLauncher = javaToolchains.launcherFor {
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
}
|
}
|
||||||
minecraftVersion "1.21.3"
|
minecraftVersion "1.21.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
expand("version": version)
|
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 {
|
dependencies {
|
||||||
compileOnly "me.clip:placeholderapi:2.11.6" // Placeholder support
|
compileOnly "me.clip:placeholderapi:2.11.6" // Placeholder support
|
||||||
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
|
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
|
||||||
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
|
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 "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
|
||||||
implementation "lol.pyr:director-adventure:2.1.2" // Commands
|
implementation "lol.pyr:director-adventure:2.1.2" // Commands
|
||||||
|
|
||||||
// Fancy text library
|
// Fancy text library
|
||||||
implementation "net.kyori:adventure-platform-bukkit:4.3.4"
|
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")
|
implementation project(":api")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
gitBranch = System.getenv('GIT_BRANCH') ?: ''
|
||||||
|
gitCommitHash = System.getenv('GIT_COMMIT') ?: ''
|
||||||
|
buildId = System.getenv('BUILD_ID') ?: ''
|
||||||
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archivesBaseName = "ZNPCsPlus"
|
archivesBaseName = "ZNPCsPlus"
|
||||||
archiveClassifier.set ""
|
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 "org.objectweb.asm", "lol.pyr.znpcsplus.libraries.asm"
|
||||||
relocate "me.lucko.jarrelocator", "lol.pyr.znpcsplus.libraries.jarrelocator"
|
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.FoliaScheduler;
|
||||||
import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
|
import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
|
||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
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.MojangSkinCache;
|
||||||
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
|
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||||
|
@ -93,7 +94,7 @@ public class ZNpcsPlus {
|
||||||
packetEvents.load();
|
packetEvents.load();
|
||||||
|
|
||||||
configManager = new ConfigManager(getDataFolder());
|
configManager = new ConfigManager(getDataFolder());
|
||||||
skinCache = new MojangSkinCache(configManager);
|
skinCache = new MojangSkinCache(configManager, new File(getDataFolder(), "skins"));
|
||||||
propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
|
propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
|
||||||
|
|
||||||
NpcPropertyRegistryProvider.register(propertyRegistry);
|
NpcPropertyRegistryProvider.register(propertyRegistry);
|
||||||
|
@ -135,8 +136,9 @@ public class ZNpcsPlus {
|
||||||
ActionRegistryImpl actionRegistry = new ActionRegistryImpl();
|
ActionRegistryImpl actionRegistry = new ActionRegistryImpl();
|
||||||
ActionFactoryImpl actionFactory = new ActionFactoryImpl(scheduler, adventure, textSerializer, bungeeConnector);
|
ActionFactoryImpl actionFactory = new ActionFactoryImpl(scheduler, adventure, textSerializer, bungeeConnector);
|
||||||
NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
|
NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
|
||||||
|
NpcSerializerRegistryImpl serializerRegistry = new NpcSerializerRegistryImpl(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||||
NpcRegistryImpl npcRegistry = new NpcRegistryImpl(configManager, this, packetFactory, actionRegistry,
|
NpcRegistryImpl npcRegistry = new NpcRegistryImpl(configManager, this, packetFactory, actionRegistry,
|
||||||
scheduler, typeRegistry, propertyRegistry, textSerializer);
|
scheduler, typeRegistry, propertyRegistry, serializerRegistry, textSerializer);
|
||||||
shutdownTasks.add(npcRegistry::unload);
|
shutdownTasks.add(npcRegistry::unload);
|
||||||
|
|
||||||
UserManager userManager = new UserManager();
|
UserManager userManager = new UserManager();
|
||||||
|
@ -159,7 +161,7 @@ public class ZNpcsPlus {
|
||||||
pluginManager.registerEvents(new UserListener(userManager), bootstrap);
|
pluginManager.registerEvents(new UserListener(userManager), bootstrap);
|
||||||
|
|
||||||
registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
|
registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
|
||||||
typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory);
|
typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory, serializerRegistry);
|
||||||
|
|
||||||
log(ChatColor.WHITE + " * Starting tasks...");
|
log(ChatColor.WHITE + " * Starting tasks...");
|
||||||
if (configManager.getConfig().checkForUpdates()) {
|
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(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
|
||||||
log("");
|
log("");
|
||||||
|
|
||||||
|
@ -246,7 +248,7 @@ public class ZNpcsPlus {
|
||||||
private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
|
private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
|
||||||
ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry,
|
ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry,
|
||||||
EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,
|
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));
|
Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED));
|
||||||
CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
|
CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
|
||||||
|
@ -296,6 +298,7 @@ public class ZNpcsPlus {
|
||||||
registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage);
|
registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage);
|
||||||
registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage);
|
registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage);
|
||||||
registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage);
|
registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage);
|
||||||
|
registerEnumParser(manager, SkeletonType.class, incorrectUsageMessage);
|
||||||
|
|
||||||
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
|
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
|
||||||
.addSubcommand("center", new CenterCommand(npcRegistry))
|
.addSubcommand("center", new CenterCommand(npcRegistry))
|
||||||
|
@ -321,7 +324,7 @@ public class ZNpcsPlus {
|
||||||
.addSubcommand("save", new SaveAllCommand(npcRegistry))
|
.addSubcommand("save", new SaveAllCommand(npcRegistry))
|
||||||
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
|
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
|
||||||
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))
|
.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("holo", new MultiCommand(bootstrap.loadHelpMessage("holo"))
|
||||||
.addSubcommand("add", new HoloAddCommand(npcRegistry))
|
.addSubcommand("add", new HoloAddCommand(npcRegistry))
|
||||||
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
|
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
|
||||||
|
@ -332,13 +335,16 @@ public class ZNpcsPlus {
|
||||||
.addSubcommand("set", new HoloSetCommand(npcRegistry))
|
.addSubcommand("set", new HoloSetCommand(npcRegistry))
|
||||||
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
|
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
|
||||||
.addSubcommand("offset", new HoloOffsetCommand(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("action", new MultiCommand(bootstrap.loadHelpMessage("action"))
|
||||||
.addSubcommand("add", new ActionAddCommand(npcRegistry, actionRegistry))
|
.addSubcommand("add", new ActionAddCommand(npcRegistry, actionRegistry))
|
||||||
.addSubcommand("clear", new ActionClearCommand(npcRegistry))
|
.addSubcommand("clear", new ActionClearCommand(npcRegistry))
|
||||||
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
|
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
|
||||||
.addSubcommand("edit", new ActionEditCommand(npcRegistry, actionRegistry))
|
.addSubcommand("edit", new ActionEditCommand(npcRegistry, actionRegistry))
|
||||||
.addSubcommand("list", new ActionListCommand(npcRegistry)))
|
.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.interaction.ActionRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||||
|
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.skin.SkinDescriptorFactoryImpl;
|
import lol.pyr.znpcsplus.skin.SkinDescriptorFactoryImpl;
|
||||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
|
|
||||||
|
@ -22,14 +23,16 @@ public class ZNpcsPlusApi implements NpcApi {
|
||||||
private final ActionRegistryImpl actionRegistry;
|
private final ActionRegistryImpl actionRegistry;
|
||||||
private final ActionFactoryImpl actionFactory;
|
private final ActionFactoryImpl actionFactory;
|
||||||
private final SkinDescriptorFactoryImpl skinDescriptorFactory;
|
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.npcRegistry = npcRegistry;
|
||||||
this.typeRegistry = typeRegistry;
|
this.typeRegistry = typeRegistry;
|
||||||
this.propertyRegistry = propertyRegistry;
|
this.propertyRegistry = propertyRegistry;
|
||||||
this.actionRegistry = actionRegistry;
|
this.actionRegistry = actionRegistry;
|
||||||
this.actionFactory = actionFactory;
|
this.actionFactory = actionFactory;
|
||||||
this.skinDescriptorFactory = new SkinDescriptorFactoryImpl(skinCache);
|
this.skinDescriptorFactory = new SkinDescriptorFactoryImpl(skinCache);
|
||||||
|
this.npcSerializerRegistry = npcSerializerRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,4 +65,9 @@ public class ZNpcsPlusApi implements NpcApi {
|
||||||
public SkinDescriptorFactory getSkinDescriptorFactory() {
|
public SkinDescriptorFactory getSkinDescriptorFactory() {
|
||||||
return skinDescriptorFactory;
|
return skinDescriptorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NpcSerializerRegistryImpl getNpcSerializerRegistry() {
|
||||||
|
return npcSerializerRegistry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,18 @@ public class CreateCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CommandContext context) throws CommandExecutionException {
|
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();
|
Player player = context.ensureSenderIsPlayer();
|
||||||
|
|
||||||
String id = context.popString();
|
String id = context.popString();
|
||||||
if (npcRegistry.getById(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED));
|
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()));
|
NpcEntryImpl entry = npcRegistry.create(id, player.getWorld(), type, new NpcLocation(player.getLocation()));
|
||||||
entry.enableEverything();
|
entry.enableEverything();
|
||||||
|
|
|
@ -17,10 +17,14 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SkinCommand implements CommandHandler {
|
public class SkinCommand implements CommandHandler {
|
||||||
private final MojangSkinCache skinCache;
|
private final MojangSkinCache skinCache;
|
||||||
|
@ -90,6 +94,30 @@ public class SkinCommand implements CommandHandler {
|
||||||
context.send(Component.text("Invalid url!", NamedTextColor.RED));
|
context.send(Component.text("Invalid url!", NamedTextColor.RED));
|
||||||
}
|
}
|
||||||
return;
|
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"));
|
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
|
@Override
|
||||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||||
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
|
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.matchSuggestion("*", "static")) return context.suggestPlayers();
|
||||||
if (context.argSize() == 3 && context.matchSuggestion("*", "url")) {
|
if (context.argSize() == 3 && context.matchSuggestion("*", "url")) {
|
||||||
return context.suggestLiteral("slim", "classic");
|
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();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,14 @@ public class ToggleCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CommandContext context) throws CommandExecutionException {
|
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();
|
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);
|
npc.setEnabled(enabled);
|
||||||
context.send(Component.text("NPC has been " + (enabled ? "enabled" : "disabled"), NamedTextColor.GREEN));
|
context.send(Component.text("NPC has been " + (enabled ? "enabled" : "disabled"), NamedTextColor.GREEN));
|
||||||
}
|
}
|
||||||
|
@ -31,6 +36,7 @@ public class ToggleCommand implements CommandHandler {
|
||||||
@Override
|
@Override
|
||||||
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
public List<String> suggest(CommandContext context) throws CommandExecutionException {
|
||||||
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
|
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
|
||||||
|
if (context.argSize() == 2) return context.suggestLiteral("enable", "disable");
|
||||||
return Collections.emptyList();
|
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.director.common.command.CommandExecutionException;
|
||||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
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.NpcEntryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcImpl;
|
import lol.pyr.znpcsplus.npc.NpcImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||||
|
@ -124,6 +125,15 @@ public class PropertySetCommand implements CommandHandler {
|
||||||
value = context.parse(type);
|
value = context.parse(type);
|
||||||
valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString();
|
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 {
|
else {
|
||||||
try {
|
try {
|
||||||
value = context.parse(type);
|
value = context.parse(type);
|
||||||
|
|
|
@ -7,13 +7,13 @@ import lol.pyr.znpcsplus.conversion.DataImporter;
|
||||||
import lol.pyr.znpcsplus.conversion.DataImporterRegistry;
|
import lol.pyr.znpcsplus.conversion.DataImporterRegistry;
|
||||||
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class ImportCommand implements CommandHandler {
|
public class ImportCommand implements CommandHandler {
|
||||||
private final NpcRegistryImpl npcRegistry;
|
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: " +
|
if (importer == null) context.halt(Component.text("Importer not found! Possible importers: " +
|
||||||
String.join(", ", importerRegistry.getIds()), NamedTextColor.RED));
|
String.join(", ", importerRegistry.getIds()), NamedTextColor.RED));
|
||||||
|
|
||||||
CompletableFuture.runAsync(() -> {
|
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||||
if (!importer.isValid()) {
|
if (!importer.isValid()) {
|
||||||
context.send(Component.text("There is no data to import from this importer!", NamedTextColor.RED));
|
context.send(Component.text("There is no data to import from this importer!", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,12 +4,12 @@ import lol.pyr.director.adventure.command.CommandContext;
|
||||||
import lol.pyr.director.adventure.command.CommandHandler;
|
import lol.pyr.director.adventure.command.CommandHandler;
|
||||||
import lol.pyr.director.common.command.CommandExecutionException;
|
import lol.pyr.director.common.command.CommandExecutionException;
|
||||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class LoadAllCommand implements CommandHandler {
|
public class LoadAllCommand implements CommandHandler {
|
||||||
private final NpcRegistryImpl npcRegistry;
|
private final NpcRegistryImpl npcRegistry;
|
||||||
|
@ -20,7 +20,7 @@ public class LoadAllCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CommandContext context) throws CommandExecutionException {
|
public void run(CommandContext context) throws CommandExecutionException {
|
||||||
CompletableFuture.runAsync(() -> {
|
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||||
npcRegistry.reload();
|
npcRegistry.reload();
|
||||||
context.send(Component.text("All NPCs have been re-loaded from storage", NamedTextColor.GREEN));
|
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.NpcRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
|
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
@ -35,8 +36,9 @@ public class MigrateCommand implements CommandHandler {
|
||||||
private final NpcStorage currentStorage;
|
private final NpcStorage currentStorage;
|
||||||
private final NpcStorageType currentStorageType;
|
private final NpcStorageType currentStorageType;
|
||||||
private final NpcRegistryImpl npcRegistry;
|
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.configManager = configManager;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.packetFactory = packetFactory;
|
this.packetFactory = packetFactory;
|
||||||
|
@ -47,6 +49,7 @@ public class MigrateCommand implements CommandHandler {
|
||||||
this.currentStorage = currentStorage;
|
this.currentStorage = currentStorage;
|
||||||
this.currentStorageType = currentStorageType;
|
this.currentStorageType = currentStorageType;
|
||||||
this.npcRegistry = npcRegistry;
|
this.npcRegistry = npcRegistry;
|
||||||
|
this.serializerRegistry = serializerRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -63,7 +66,7 @@ public class MigrateCommand implements CommandHandler {
|
||||||
if (currentStorageType == from) {
|
if (currentStorageType == from) {
|
||||||
fromStorage = currentStorage;
|
fromStorage = currentStorage;
|
||||||
} else {
|
} 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) {
|
if (fromStorage == null) {
|
||||||
context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED));
|
context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
@ -84,7 +87,7 @@ public class MigrateCommand implements CommandHandler {
|
||||||
if (currentStorageType == to) {
|
if (currentStorageType == to) {
|
||||||
toStorage = currentStorage;
|
toStorage = currentStorage;
|
||||||
} else {
|
} 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) {
|
if (toStorage == null) {
|
||||||
context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED));
|
context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,12 +4,12 @@ import lol.pyr.director.adventure.command.CommandContext;
|
||||||
import lol.pyr.director.adventure.command.CommandHandler;
|
import lol.pyr.director.adventure.command.CommandHandler;
|
||||||
import lol.pyr.director.common.command.CommandExecutionException;
|
import lol.pyr.director.common.command.CommandExecutionException;
|
||||||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class SaveAllCommand implements CommandHandler {
|
public class SaveAllCommand implements CommandHandler {
|
||||||
private final NpcRegistryImpl npcRegistry;
|
private final NpcRegistryImpl npcRegistry;
|
||||||
|
@ -20,7 +20,7 @@ public class SaveAllCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CommandContext context) throws CommandExecutionException {
|
public void run(CommandContext context) throws CommandExecutionException {
|
||||||
CompletableFuture.runAsync(() -> {
|
FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||||
npcRegistry.save();
|
npcRegistry.save();
|
||||||
context.send(Component.text("All NPCs have been saved to storage", NamedTextColor.GREEN));
|
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")) {
|
if (toggleValues.containsKey("mirror")) {
|
||||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
|
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
|
||||||
}
|
}
|
||||||
if (toggleValues.containsKey("glow")) {
|
if (toggleValues.containsKey("glow") && (boolean) toggleValues.get("glow")) {
|
||||||
|
if (!model.getGlowName().isEmpty())
|
||||||
try {
|
try {
|
||||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf((String) toggleValues.get("glow")));
|
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf(model.getGlowName()));
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE);
|
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() {
|
public String getSignature() {
|
||||||
return signature;
|
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.PacketEvents;
|
||||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
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.data.EntityDataTypes;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
|
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
|
||||||
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
|
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.api.skin.SkinDescriptor;
|
||||||
import lol.pyr.znpcsplus.config.ConfigManager;
|
import lol.pyr.znpcsplus.config.ConfigManager;
|
||||||
import lol.pyr.znpcsplus.entity.properties.*;
|
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.VillagerLevelProperty;
|
||||||
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
|
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
|
||||||
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
|
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
|
||||||
|
@ -32,18 +34,19 @@ import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1.8 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=7415">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=7968">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=8241">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=8534">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=14048">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=14800">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=15240">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=15991">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=16539">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=17521">...</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://wiki.vg/index.php?title=Entity_metadata&oldid=18191">...</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://wiki.vg/index.php?title=Entity_metadata">...</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")
|
@SuppressWarnings("unchecked")
|
||||||
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
|
@ -90,6 +93,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
registerEnumSerializer(Sound.class);
|
registerEnumSerializer(Sound.class);
|
||||||
registerEnumSerializer(ArmadilloState.class);
|
registerEnumSerializer(ArmadilloState.class);
|
||||||
registerEnumSerializer(WoldVariant.class);
|
registerEnumSerializer(WoldVariant.class);
|
||||||
|
registerEnumSerializer(SkeletonType.class);
|
||||||
|
|
||||||
registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.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", LookType.FIXED));
|
||||||
register(new DummyProperty<>("look_distance", configManager.getConfig().lookPropertyDistance()));
|
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<>("view_distance", configManager.getConfig().viewDistance()));
|
||||||
|
|
||||||
register(new DummyProperty<>("permission_required", false));
|
register(new DummyProperty<>("permission_required", false));
|
||||||
|
@ -151,6 +156,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
linkProperties("glow", "fire", "invisible");
|
linkProperties("glow", "fire", "invisible");
|
||||||
register(new BooleanProperty("silent", 4, false, legacyBooleans));
|
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;
|
final int tameableIndex;
|
||||||
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tameableIndex = 17;
|
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tameableIndex = 17;
|
||||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) tameableIndex = 16;
|
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 BooleanProperty("baby", babyIndex, false, legacyBooleans));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(new EntitySittingProperty(packetFactory, this));
|
||||||
|
|
||||||
// Player
|
// Player
|
||||||
register(new DummyProperty<>("skin", SkinDescriptor.class, false));
|
register(new DummyProperty<>("skin", SkinDescriptor.class, false));
|
||||||
final int skinLayersIndex;
|
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
|
witherIndex += 3; // skip the first 3 indexes, will be used for the other properties later
|
||||||
register(new IntegerProperty("invulnerable_time", witherIndex, 0, false));
|
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;
|
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
||||||
// Shulker
|
// Shulker
|
||||||
int shulkerIndex;
|
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_15)) llamaIndex = 20;
|
||||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) llamaIndex = 19;
|
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) llamaIndex = 19;
|
||||||
else llamaIndex = 17;
|
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));
|
register(new EncodedIntegerProperty<>("llama_variant", LlamaVariant.CREAMY, llamaIndex, Enum::ordinal));
|
||||||
|
|
||||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
|
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;
|
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21)) return;
|
||||||
|
|
||||||
|
register(new EquipmentProperty(packetFactory, "body", EquipmentSlot.BODY));
|
||||||
|
|
||||||
// Bogged
|
// Bogged
|
||||||
register(new BooleanProperty("bogged_sheared", 16, false, legacyBooleans));
|
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) {
|
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.api.entity.PropertyHolder;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.reflection.Reflections;
|
import lol.pyr.znpcsplus.reflection.Reflections;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
|
import lol.pyr.znpcsplus.util.Viewable;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Set;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PacketEntity implements PropertyHolder {
|
public class PacketEntity implements PropertyHolder {
|
||||||
private final PacketFactory packetFactory;
|
private final PacketFactory packetFactory;
|
||||||
|
|
||||||
private final PropertyHolder properties;
|
private final PropertyHolder properties;
|
||||||
|
private final Viewable viewable;
|
||||||
private final int entityId;
|
private final int entityId;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
|
|
||||||
private final EntityType type;
|
private final EntityType type;
|
||||||
private NpcLocation location;
|
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.packetFactory = packetFactory;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
this.viewable = viewable;
|
||||||
this.entityId = reserveEntityID();
|
this.entityId = reserveEntityID();
|
||||||
this.uuid = UUID.randomUUID();
|
this.uuid = UUID.randomUUID();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -51,22 +58,117 @@ public class PacketEntity implements PropertyHolder {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
public void setLocation(NpcLocation location) {
|
||||||
this.location = 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) {
|
public CompletableFuture<Void> spawn(Player player) {
|
||||||
if (type == EntityTypes.PLAYER) packetFactory.spawnPlayer(player, this, properties);
|
return FutureUtil.exceptionPrintingRunAsync(() -> {
|
||||||
|
if (type == EntityTypes.PLAYER) packetFactory.spawnPlayer(player, this, properties).join();
|
||||||
else packetFactory.spawnEntity(player, this, properties);
|
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) {
|
public void setHeadRotation(Player player, float yaw, float pitch) {
|
||||||
packetFactory.sendHeadRotation(player, this, yaw, 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) {
|
public void despawn(Player player) {
|
||||||
packetFactory.destroyEntity(player, this, properties);
|
packetFactory.destroyEntity(player, this, properties);
|
||||||
|
if (vehicle != null) vehicle.despawn(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshMeta(Player 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);
|
EntityData oldData = properties.get(0);
|
||||||
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
|
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
|
||||||
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (value == null ? 0 : 0x40))));
|
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (value == null ? 0 : 0x40))));
|
||||||
if (isSpawned) packetFactory.removeTeam(player, entity);
|
// 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);
|
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.config.ConfigManager;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
import lol.pyr.znpcsplus.util.Viewable;
|
import lol.pyr.znpcsplus.util.Viewable;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
@ -16,6 +17,8 @@ import org.bukkit.entity.Player;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class HologramImpl extends Viewable implements Hologram {
|
public class HologramImpl extends Viewable implements Hologram {
|
||||||
private final ConfigManager configManager;
|
private final ConfigManager configManager;
|
||||||
|
@ -38,9 +41,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTextLineComponent(Component line) {
|
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);
|
lines.add(newLine);
|
||||||
relocateLines(newLine);
|
relocateLines();
|
||||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +60,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItemLinePEStack(ItemStack item) {
|
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);
|
lines.add(newLine);
|
||||||
relocateLines(newLine);
|
relocateLines();
|
||||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +101,16 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
lines.clear();
|
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) {
|
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);
|
lines.add(index, newLine);
|
||||||
relocateLines(newLine);
|
relocateLines();
|
||||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
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) {
|
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);
|
lines.add(index, newLine);
|
||||||
relocateLines(newLine);
|
relocateLines();
|
||||||
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +147,10 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void UNSAFE_show(Player player) {
|
protected CompletableFuture<Void> UNSAFE_show(Player player) {
|
||||||
for (HologramLine<?> line : lines) line.show(player);
|
return FutureUtil.allOf(lines.stream()
|
||||||
|
.map(line -> line.show(player))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -172,14 +183,10 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void relocateLines() {
|
private void relocateLines() {
|
||||||
relocateLines(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void relocateLines(HologramLine<?> newLine) {
|
|
||||||
final double lineSpacing = configManager.getConfig().lineSpacing();
|
final double lineSpacing = configManager.getConfig().lineSpacing();
|
||||||
double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset();
|
double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset();
|
||||||
for (HologramLine<?> line : lines) {
|
for (HologramLine<?> line : lines) {
|
||||||
line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers());
|
line.setLocation(location.withY(height));
|
||||||
height -= lineSpacing;
|
height -= lineSpacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,11 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
import org.bukkit.entity.Player;
|
import lol.pyr.znpcsplus.util.Viewable;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class HologramItem extends HologramLine<ItemStack> {
|
public class HologramItem extends HologramLine<ItemStack> {
|
||||||
public HologramItem(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
|
public HologramItem(Viewable viewable, EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
|
||||||
super(item, packetFactory, EntityTypes.ITEM, location);
|
super(viewable, item, packetFactory, EntityTypes.ITEM, location);
|
||||||
addProperty(propertyRegistry.getByName("holo_item"));
|
addProperty(propertyRegistry.getByName("holo_item"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +31,8 @@ public class HologramItem extends HologramLine<ItemStack> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
public void setLocation(NpcLocation location) {
|
||||||
super.setLocation(location.withY(location.getY() + 2.05), viewers);
|
super.setLocation(location.withY(location.getY() + 2.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean ensureValidItemInput(String in) {
|
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.entity.PacketEntity;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
|
import lol.pyr.znpcsplus.util.Viewable;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class HologramLine<M> implements PropertyHolder {
|
public class HologramLine<M> implements PropertyHolder {
|
||||||
private M value;
|
private M value;
|
||||||
private final PacketEntity entity;
|
private final PacketEntity entity;
|
||||||
private final Set<EntityProperty<?>> properties;
|
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.value = value;
|
||||||
this.entity = new PacketEntity(packetFactory, this, type, location);
|
this.entity = new PacketEntity(packetFactory, this, viewable, type, location);
|
||||||
this.properties = new HashSet<>();
|
this.properties = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,16 +38,16 @@ public class HologramLine<M> implements PropertyHolder {
|
||||||
entity.refreshMeta(player);
|
entity.refreshMeta(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void show(Player player) {
|
protected CompletableFuture<Void> show(Player player) {
|
||||||
entity.spawn(player);
|
return entity.spawn(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void hide(Player player) {
|
protected void hide(Player player) {
|
||||||
entity.despawn(player);
|
entity.despawn(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(NpcLocation location, Collection<Player> viewers) {
|
public void setLocation(NpcLocation location) {
|
||||||
entity.setLocation(location, viewers);
|
entity.setLocation(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getEntityId() {
|
public int getEntityId() {
|
||||||
|
|
|
@ -5,24 +5,26 @@ import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
import lol.pyr.znpcsplus.util.NpcLocation;
|
||||||
|
import lol.pyr.znpcsplus.util.Viewable;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class HologramText extends HologramLine<Component> {
|
public class HologramText extends HologramLine<Component> {
|
||||||
|
|
||||||
private static final Component BLANK = Component.text("%blank%");
|
private static final Component BLANK = Component.text("%blank%");
|
||||||
|
|
||||||
public HologramText(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
|
public HologramText(Viewable viewable, EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
|
||||||
super(text, packetFactory, EntityTypes.ARMOR_STAND, location);
|
super(viewable, text, packetFactory, EntityTypes.ARMOR_STAND, location);
|
||||||
addProperty(propertyRegistry.getByName("name"));
|
addProperty(propertyRegistry.getByName("name"));
|
||||||
addProperty(propertyRegistry.getByName("invisible"));
|
addProperty(propertyRegistry.getByName("invisible"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show(Player player) {
|
public CompletableFuture<Void> show(Player player) {
|
||||||
if (!getValue().equals(BLANK)) {
|
if (getValue().equals(BLANK)) return CompletableFuture.completedFuture(null);
|
||||||
super.show(player);
|
return super.show(player);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -37,4 +39,12 @@ public class HologramText extends HologramLine<Component> {
|
||||||
public boolean hasProperty(EntityProperty<?> key) {
|
public boolean hasProperty(EntityProperty<?> key) {
|
||||||
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible");
|
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,
|
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
Component.text("Click to edit this action", NamedTextColor.GRAY)))
|
Component.text("Click to edit this action", NamedTextColor.GRAY)))
|
||||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
.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(" | ", NamedTextColor.GRAY))
|
||||||
.append(Component.text("[DELETE]", NamedTextColor.RED)
|
.append(Component.text("[DELETE]", NamedTextColor.RED)
|
||||||
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT,
|
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
|
|
|
@ -20,9 +20,12 @@ import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NpcImpl extends Viewable implements Npc {
|
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 Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
|
||||||
private final List<InteractionAction> actions = new ArrayList<>();
|
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) {
|
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);
|
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.type = type;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.uuid = uuid;
|
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()));
|
hologram = new HologramImpl(propertyRegistry, configManager, packetFactory, textSerializer, location.withY(location.getY() + type.getHologramOffset()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(NpcTypeImpl type) {
|
public void setType(NpcTypeImpl type) {
|
||||||
UNSAFE_hideAll();
|
UNSAFE_hideAll();
|
||||||
this.type = type;
|
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()));
|
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset()));
|
||||||
UNSAFE_showAll();
|
UNSAFE_showAll();
|
||||||
}
|
}
|
||||||
|
@ -85,20 +90,34 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
|
|
||||||
public void setLocation(NpcLocation location) {
|
public void setLocation(NpcLocation location) {
|
||||||
this.location = 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()));
|
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeadRotation(Player player, float yaw, float pitch) {
|
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);
|
entity.setHeadRotation(player, yaw, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeadRotation(float yaw, float pitch) {
|
public void setHeadRotation(float yaw, float pitch) {
|
||||||
for (Player player : getViewers()) {
|
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);
|
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() {
|
public HologramImpl getHologram() {
|
||||||
return hologram;
|
return hologram;
|
||||||
}
|
}
|
||||||
|
@ -125,13 +144,14 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void UNSAFE_show(Player player) {
|
protected CompletableFuture<Void> UNSAFE_show(Player player) {
|
||||||
entity.spawn(player);
|
playerLookMap.put(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()});
|
||||||
hologram.show(player);
|
return CompletableFuture.allOf(entity.spawn(player), hologram.show(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void UNSAFE_hide(Player player) {
|
protected void UNSAFE_hide(Player player) {
|
||||||
|
playerLookMap.remove(player.getUniqueId());
|
||||||
entity.despawn(player);
|
entity.despawn(player);
|
||||||
hologram.hide(player);
|
hologram.hide(player);
|
||||||
}
|
}
|
||||||
|
@ -246,4 +266,29 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
public void swingHand(boolean offHand) {
|
public void swingHand(boolean offHand) {
|
||||||
for (Player viewer : getViewers()) entity.swingHand(viewer, 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.interaction.ActionRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||||
|
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
import lol.pyr.znpcsplus.storage.NpcStorageType;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
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<String, NpcEntryImpl> npcIdLookupMap = new HashMap<>();
|
||||||
private final Map<UUID, NpcEntryImpl> npcUuidLookupMap = 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.textSerializer = textSerializer;
|
||||||
this.propertyRegistry = propertyRegistry;
|
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) {
|
if (storage == null) {
|
||||||
Bukkit.getLogger().warning("Failed to initialize storage, falling back to YAML");
|
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.packetFactory = packetFactory;
|
||||||
this.configManager = configManager;
|
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) {
|
private void register(NpcEntryImpl entry) {
|
||||||
|
if (entry == null) throw new NullPointerException();
|
||||||
unregister(npcIdLookupMap.put(entry.getId(), entry));
|
unregister(npcIdLookupMap.put(entry.getId(), entry));
|
||||||
unregister(npcUuidLookupMap.put(entry.getNpc().getUuid(), entry));
|
unregister(npcUuidLookupMap.put(entry.getNpc().getUuid(), entry));
|
||||||
npcList.add(entry);
|
npcList.add(entry);
|
||||||
|
|
|
@ -114,12 +114,15 @@ public class NpcTypeImpl implements NpcType {
|
||||||
|
|
||||||
public NpcTypeImpl build() {
|
public NpcTypeImpl build() {
|
||||||
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
|
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",
|
"potion_color", "potion_ambient", "display_name", "permission_required",
|
||||||
"player_knockback", "player_knockback_exempt_permission", "player_knockback_distance", "player_knockback_vertical",
|
"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_horizontal", "player_knockback_cooldown", "player_knockback_sound", "player_knockback_sound_name",
|
||||||
"player_knockback_sound_volume", "player_knockback_sound_pitch");
|
"player_knockback_sound_volume", "player_knockback_sound_pitch");
|
||||||
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
|
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
|
// 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_9)) addProperties("glow");
|
||||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) {
|
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)) {
|
} else if (version.isOlderThan(ServerVersion.V_1_11) && type.equals(EntityTypes.HORSE)) {
|
||||||
addProperties("has_chest");
|
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)) {
|
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_EVO_ILLU_ILLAGER)) {
|
||||||
addProperties("spell");
|
addProperties("spell");
|
||||||
}
|
}
|
||||||
|
@ -176,6 +182,11 @@ public class NpcTypeImpl implements NpcType {
|
||||||
addProperties("wolf_variant");
|
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);
|
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)
|
register(builder(p, "player", EntityTypes.PLAYER)
|
||||||
.setHologramOffset(-0.15D)
|
.setHologramOffset(-0.15D)
|
||||||
.addEquipmentProperties()
|
.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_cape", true)
|
||||||
.addDefaultProperty("skin_jacket", true)
|
.addDefaultProperty("skin_jacket", true)
|
||||||
.addDefaultProperty("skin_left_sleeve", true)
|
.addDefaultProperty("skin_left_sleeve", true)
|
||||||
|
@ -82,7 +82,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "enderman", EntityTypes.ENDERMAN)
|
register(builder(p, "enderman", EntityTypes.ENDERMAN)
|
||||||
.setHologramOffset(0.925)
|
.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)
|
register(builder(p, "endermite", EntityTypes.ENDERMITE)
|
||||||
.setHologramOffset(-1.675));
|
.setHologramOffset(-1.675));
|
||||||
|
@ -93,7 +93,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "giant", EntityTypes.GIANT)
|
register(builder(p, "giant", EntityTypes.GIANT)
|
||||||
.setHologramOffset(10.025)
|
.setHologramOffset(10.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "guardian", EntityTypes.GUARDIAN)
|
register(builder(p, "guardian", EntityTypes.GUARDIAN)
|
||||||
.setHologramOffset(-1.125)
|
.setHologramOffset(-1.125)
|
||||||
|
@ -133,7 +134,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "skeleton", EntityTypes.SKELETON)
|
register(builder(p, "skeleton", EntityTypes.SKELETON)
|
||||||
.setHologramOffset(0.015)
|
.setHologramOffset(0.015)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "skeleton_horse", EntityTypes.SKELETON_HORSE)
|
register(builder(p, "skeleton_horse", EntityTypes.SKELETON_HORSE)
|
||||||
.setHologramOffset(-0.375));
|
.setHologramOffset(-0.375));
|
||||||
|
@ -169,14 +171,16 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "zombie", EntityTypes.ZOMBIE)
|
register(builder(p, "zombie", EntityTypes.ZOMBIE)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "zombie_horse", EntityTypes.ZOMBIE_HORSE)
|
register(builder(p, "zombie_horse", EntityTypes.ZOMBIE_HORSE)
|
||||||
.setHologramOffset(-0.375));
|
.setHologramOffset(-0.375));
|
||||||
|
|
||||||
register(builder(p, "zombified_piglin", EntityTypes.ZOMBIFIED_PIGLIN)
|
register(builder(p, "zombified_piglin", EntityTypes.ZOMBIFIED_PIGLIN)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
if (!version.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
||||||
|
|
||||||
|
@ -203,21 +207,21 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "husk", EntityTypes.HUSK)
|
register(builder(p, "husk", EntityTypes.HUSK)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
register(builder(p, "polar_bear", EntityTypes.POLAR_BEAR)
|
|
||||||
.setHologramOffset(-0.575));
|
|
||||||
|
|
||||||
register(builder(p, "stray", EntityTypes.STRAY)
|
register(builder(p, "stray", EntityTypes.STRAY)
|
||||||
.setHologramOffset(0.015)
|
.setHologramOffset(0.015)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "evoker", EntityTypes.EVOKER)
|
register(builder(p, "evoker", EntityTypes.EVOKER)
|
||||||
.setHologramOffset(-0.025));
|
.setHologramOffset(-0.025)
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "llama", EntityTypes.LLAMA)
|
register(builder(p, "llama", EntityTypes.LLAMA)
|
||||||
.setHologramOffset(-0.105)
|
.setHologramOffset(-0.105)
|
||||||
.addProperties("carpet_color", "llama_variant"));
|
.addProperties("carpet_color", "llama_variant", "body"));
|
||||||
|
|
||||||
register(builder(p, "vex", EntityTypes.VEX)
|
register(builder(p, "vex", EntityTypes.VEX)
|
||||||
.setHologramOffset(-1.175)
|
.setHologramOffset(-1.175)
|
||||||
|
@ -225,20 +229,23 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "vindicator", EntityTypes.VINDICATOR)
|
register(builder(p, "vindicator", EntityTypes.VINDICATOR)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addProperties("celebrating"));
|
.addProperties("celebrating", "entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "wither_skeleton", EntityTypes.WITHER_SKELETON)
|
register(builder(p, "wither_skeleton", EntityTypes.WITHER_SKELETON)
|
||||||
.setHologramOffset(0.425)
|
.setHologramOffset(0.425)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "zombie_villager", EntityTypes.ZOMBIE_VILLAGER)
|
register(builder(p, "zombie_villager", EntityTypes.ZOMBIE_VILLAGER)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
|
if (!version.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
|
||||||
|
|
||||||
register(builder(p, "illusioner", EntityTypes.ILLUSIONER)
|
register(builder(p, "illusioner", EntityTypes.ILLUSIONER)
|
||||||
.setHologramOffset(-0.025));
|
.setHologramOffset(-0.025)
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "parrot", EntityTypes.PARROT)
|
register(builder(p, "parrot", EntityTypes.PARROT)
|
||||||
.setHologramOffset(-1.075)
|
.setHologramOffset(-1.075)
|
||||||
|
@ -255,7 +262,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "drowned", EntityTypes.DROWNED)
|
register(builder(p, "drowned", EntityTypes.DROWNED)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "phantom", EntityTypes.PHANTOM)
|
register(builder(p, "phantom", EntityTypes.PHANTOM)
|
||||||
.setHologramOffset(-1.475));
|
.setHologramOffset(-1.475));
|
||||||
|
@ -291,7 +299,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
register(builder(p, "pillager", EntityTypes.PILLAGER)
|
register(builder(p, "pillager", EntityTypes.PILLAGER)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addHandProperties()
|
.addHandProperties()
|
||||||
.addProperties("pillager_charging"));
|
.addProperties("pillager_charging", "entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "ravager", EntityTypes.RAVAGER)
|
register(builder(p, "ravager", EntityTypes.RAVAGER)
|
||||||
.setHologramOffset(0.225));
|
.setHologramOffset(0.225));
|
||||||
|
@ -319,11 +327,12 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
register(builder(p, "piglin", EntityTypes.PIGLIN)
|
register(builder(p, "piglin", EntityTypes.PIGLIN)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties()
|
.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)
|
register(builder(p, "piglin_brute", EntityTypes.PIGLIN_BRUTE)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
.addEquipmentProperties());
|
.addEquipmentProperties()
|
||||||
|
.addProperties("entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "strider", EntityTypes.STRIDER)
|
register(builder(p, "strider", EntityTypes.STRIDER)
|
||||||
.setHologramOffset(-0.275)
|
.setHologramOffset(-0.275)
|
||||||
|
@ -372,6 +381,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
.setHologramOffset(0.4)
|
.setHologramOffset(0.4)
|
||||||
.addProperties("bashing", "camel_sitting"));
|
.addProperties("bashing", "camel_sitting"));
|
||||||
|
|
||||||
|
|
||||||
if (!version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return;
|
if (!version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return;
|
||||||
|
|
||||||
register(builder(p, "armadillo", EntityTypes.ARMADILLO)
|
register(builder(p, "armadillo", EntityTypes.ARMADILLO)
|
||||||
|
@ -382,10 +392,16 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
||||||
|
|
||||||
register(builder(p, "bogged", EntityTypes.BOGGED)
|
register(builder(p, "bogged", EntityTypes.BOGGED)
|
||||||
.setHologramOffset(0.015)
|
.setHologramOffset(0.015)
|
||||||
.addProperties("bogged_sheared"));
|
.addProperties("bogged_sheared", "entity_sitting"));
|
||||||
|
|
||||||
register(builder(p, "breeze", EntityTypes.BREEZE)
|
register(builder(p, "breeze", EntityTypes.BREEZE)
|
||||||
.setHologramOffset(-0.205));
|
.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() {
|
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.entity.data.EntityData;
|
||||||
import com.github.retrooper.packetevents.protocol.player.Equipment;
|
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.api.entity.PropertyHolder;
|
||||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||||
import lol.pyr.znpcsplus.util.NamedColor;
|
import lol.pyr.znpcsplus.util.NamedColor;
|
||||||
|
@ -11,7 +12,7 @@ import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public interface PacketFactory {
|
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 spawnEntity(Player player, PacketEntity entity, PropertyHolder properties);
|
||||||
void destroyEntity(Player player, PacketEntity entity, PropertyHolder properties);
|
void destroyEntity(Player player, PacketEntity entity, PropertyHolder properties);
|
||||||
void teleportEntity(Player player, PacketEntity entity);
|
void teleportEntity(Player player, PacketEntity entity);
|
||||||
|
@ -24,4 +25,7 @@ public interface PacketFactory {
|
||||||
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
|
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
|
||||||
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
|
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
|
||||||
void sendHandSwing(Player player, PacketEntity entity, boolean offHand);
|
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;
|
package lol.pyr.znpcsplus.packets;
|
||||||
|
|
||||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
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.util.Vector3d;
|
||||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
|
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
|
||||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
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(),
|
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())));
|
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
|
||||||
sendAllMetadata(player, entity, properties);
|
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)));
|
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 org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
|
public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
|
||||||
|
|
||||||
|
@ -27,14 +28,15 @@ public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
public CompletableFuture<Void> spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||||
addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
return addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||||
NpcLocation location = entity.getLocation();
|
NpcLocation location = entity.getLocation();
|
||||||
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
|
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())));
|
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
|
||||||
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
||||||
sendAllMetadata(player, entity, properties);
|
sendAllMetadata(player, entity, properties);
|
||||||
|
sendAllAttributes(player, entity, properties);
|
||||||
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
|
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.EntityPropertyImpl;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||||
|
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
|
||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||||
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
|
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
|
||||||
import lol.pyr.znpcsplus.util.NamedColor;
|
import lol.pyr.znpcsplus.util.NamedColor;
|
||||||
|
@ -47,14 +48,15 @@ public class V1_8PacketFactory implements PacketFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
public CompletableFuture<Void> spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||||
addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
return addTabPlayer(player, entity, properties).thenAccept(ignored -> {
|
||||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
||||||
NpcLocation location = entity.getLocation();
|
NpcLocation location = entity.getLocation();
|
||||||
sendPacket(player, new WrapperPlayServerSpawnPlayer(entity.getEntityId(),
|
sendPacket(player, new WrapperPlayServerSpawnPlayer(entity.getEntityId(),
|
||||||
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
|
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
|
||||||
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
|
||||||
sendAllMetadata(player, entity, properties);
|
sendAllMetadata(player, entity, properties);
|
||||||
|
sendAllAttributes(player, entity, properties);
|
||||||
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
|
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),
|
new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location),
|
||||||
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
|
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
|
||||||
sendAllMetadata(player, entity, properties);
|
sendAllMetadata(player, entity, properties);
|
||||||
|
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
|
||||||
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
|
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)));
|
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) {
|
protected void sendPacket(Player player, PacketWrapper<?> packet) {
|
||||||
packetEvents.getPlayerManager().sendPacket(player, packet);
|
packetEvents.getPlayerManager().sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
|
@ -182,4 +190,19 @@ public class V1_8PacketFactory implements PacketFactory {
|
||||||
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_OFF_HAND :
|
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_OFF_HAND :
|
||||||
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_MAIN_ARM));
|
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) {
|
public SkinDescriptor createUrlDescriptor(URL url, String variant) {
|
||||||
return PrefetchedDescriptor.fromUrl(skinCache, url, variant).join();
|
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.config.ConfigManager;
|
||||||
import lol.pyr.znpcsplus.reflection.Reflections;
|
import lol.pyr.znpcsplus.reflection.Reflections;
|
||||||
import lol.pyr.znpcsplus.skin.SkinImpl;
|
import lol.pyr.znpcsplus.skin.SkinImpl;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -26,9 +28,12 @@ public class MojangSkinCache {
|
||||||
|
|
||||||
private final Map<String, SkinImpl> cache = new ConcurrentHashMap<>();
|
private final Map<String, SkinImpl> cache = new ConcurrentHashMap<>();
|
||||||
private final Map<String, CachedId> idCache = 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.configManager = configManager;
|
||||||
|
this.skinsFolder = skinsFolder;
|
||||||
|
if (!skinsFolder.exists()) skinsFolder.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanCache() {
|
public void cleanCache() {
|
||||||
|
@ -42,7 +47,7 @@ public class MojangSkinCache {
|
||||||
|
|
||||||
if (idCache.containsKey(name.toLowerCase())) return fetchByUUID(idCache.get(name.toLowerCase()).getId());
|
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);
|
URL url = parseUrl("https://api.mojang.com/users/profiles/minecraft/" + name);
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
|
@ -75,7 +80,7 @@ public class MojangSkinCache {
|
||||||
|
|
||||||
if (idCache.containsKey(name.toLowerCase())) return fetchByUUID(idCache.get(name.toLowerCase()).getId());
|
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);
|
URL url = parseUrl("https://api.ashcon.app/mojang/v2/user/" + name);
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
|
@ -106,7 +111,7 @@ public class MojangSkinCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<SkinImpl> fetchByUrl(URL url, String variant) {
|
public CompletableFuture<SkinImpl> fetchByUrl(URL url, String variant) {
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return FutureUtil.exceptionPrintingSupplyAsync(() -> {
|
||||||
URL apiUrl = parseUrl("https://api.mineskin.org/generate/url");
|
URL apiUrl = parseUrl("https://api.mineskin.org/generate/url");
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
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) {
|
public boolean isNameFullyCached(String s) {
|
||||||
String name = s.toLowerCase();
|
String name = s.toLowerCase();
|
||||||
if (!idCache.containsKey(name)) return false;
|
if (!idCache.containsKey(name)) return false;
|
||||||
|
@ -170,7 +227,7 @@ public class MojangSkinCache {
|
||||||
if (!skin.isExpired()) return CompletableFuture.completedFuture(skin);
|
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");
|
URL url = parseUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false");
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
|
@ -213,4 +270,8 @@ public class MojangSkinCache {
|
||||||
throw new RuntimeException(exception);
|
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.BaseSkinDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.SkinImpl;
|
import lol.pyr.znpcsplus.skin.SkinImpl;
|
||||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
|
import lol.pyr.znpcsplus.util.FutureUtil;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@ -18,11 +20,21 @@ public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<PrefetchedDescriptor> forPlayer(MojangSkinCache cache, String name) {
|
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) {
|
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
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
|
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.storage.mysql.MySQLStorage;
|
import lol.pyr.znpcsplus.storage.mysql.MySQLStorage;
|
||||||
import lol.pyr.znpcsplus.storage.sqlite.SQLiteStorage;
|
import lol.pyr.znpcsplus.storage.sqlite.SQLiteStorage;
|
||||||
import lol.pyr.znpcsplus.storage.yaml.YamlStorage;
|
import lol.pyr.znpcsplus.storage.yaml.YamlStorage;
|
||||||
|
@ -16,13 +17,13 @@ import java.io.File;
|
||||||
public enum NpcStorageType {
|
public enum NpcStorageType {
|
||||||
YAML {
|
YAML {
|
||||||
@Override
|
@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) {
|
||||||
return new YamlStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "data"));
|
return new YamlStorage(serializerRegistry, new File(plugin.getDataFolder(), "data"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SQLITE {
|
SQLITE {
|
||||||
@Override
|
@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 {
|
try {
|
||||||
return new SQLiteStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "znpcsplus.sqlite"));
|
return new SQLiteStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "znpcsplus.sqlite"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -33,7 +34,7 @@ public enum NpcStorageType {
|
||||||
},
|
},
|
||||||
MYSQL {
|
MYSQL {
|
||||||
@Override
|
@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 {
|
try {
|
||||||
return new MySQLStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
return new MySQLStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
|
||||||
} catch (Exception e) {
|
} 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;
|
package lol.pyr.znpcsplus.storage.yaml;
|
||||||
|
|
||||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
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.NpcEntryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcImpl;
|
import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
|
||||||
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
|
|
||||||
import lol.pyr.znpcsplus.packets.PacketFactory;
|
|
||||||
import lol.pyr.znpcsplus.storage.NpcStorage;
|
import lol.pyr.znpcsplus.storage.NpcStorage;
|
||||||
import lol.pyr.znpcsplus.util.NpcLocation;
|
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.ConfigurationSection;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.logging.Level;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class YamlStorage implements NpcStorage {
|
public class YamlStorage implements NpcStorage {
|
||||||
private final static Logger logger = Logger.getLogger("YamlStorage");
|
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 File folder;
|
||||||
|
private final NpcSerializer<YamlConfiguration> yamlSerializer;
|
||||||
|
|
||||||
public YamlStorage(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, File folder) {
|
public YamlStorage(NpcSerializerRegistryImpl serializerRegistry, File folder) {
|
||||||
this.packetFactory = packetFactory;
|
this.yamlSerializer = serializerRegistry.getSerializer(YamlConfiguration.class);
|
||||||
this.configManager = configManager;
|
|
||||||
this.actionRegistry = actionRegistry;
|
|
||||||
this.typeRegistry = typeRegistry;
|
|
||||||
this.propertyRegistry = propertyRegistry;
|
|
||||||
this.textSerializer = textSerializer;
|
|
||||||
this.folder = folder;
|
this.folder = folder;
|
||||||
if (!this.folder.exists()) this.folder.mkdirs();
|
if (!this.folder.exists()) this.folder.mkdirs();
|
||||||
}
|
}
|
||||||
|
@ -53,45 +34,7 @@ public class YamlStorage implements NpcStorage {
|
||||||
List<NpcEntryImpl> npcs = new ArrayList<>(files.length);
|
List<NpcEntryImpl> npcs = new ArrayList<>(files.length);
|
||||||
for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) try {
|
for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) try {
|
||||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||||
UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
|
npcs.add((NpcEntryImpl) yamlSerializer.deserialize(config));
|
||||||
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);
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.severe("Failed to load npc file: " + file.getName());
|
logger.severe("Failed to load npc file: " + file.getName());
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
|
@ -102,43 +45,7 @@ public class YamlStorage implements NpcStorage {
|
||||||
@Override
|
@Override
|
||||||
public void saveNpcs(Collection<NpcEntryImpl> npcs) {
|
public void saveNpcs(Collection<NpcEntryImpl> npcs) {
|
||||||
for (NpcEntryImpl entry : npcs) try {
|
for (NpcEntryImpl entry : npcs) try {
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
YamlConfiguration config = yamlSerializer.serialize(entry);
|
||||||
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()));
|
|
||||||
|
|
||||||
config.save(fileFor(entry));
|
config.save(fileFor(entry));
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
logger.severe("Failed to save npc with id " + entry.getId());
|
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<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<LookType> lookProperty = propertyRegistry.getByName("look", LookType.class);
|
||||||
EntityPropertyImpl<Double> lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.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> permissionRequiredProperty = propertyRegistry.getByName("permission_required", Boolean.class);
|
||||||
EntityPropertyImpl<Boolean> playerKnockbackProperty = propertyRegistry.getByName("player_knockback", Boolean.class);
|
EntityPropertyImpl<Boolean> playerKnockbackProperty = propertyRegistry.getByName("player_knockback", Boolean.class);
|
||||||
EntityPropertyImpl<String> playerKnockbackExemptPermissionProperty = propertyRegistry.getByName("player_knockback_exempt_permission", String.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> playerKnockbackSoundVolumeProperty = propertyRegistry.getByName("player_knockback_sound_volume", Float.class);
|
||||||
EntityPropertyImpl<Float> playerKnockbackSoundPitchProperty = propertyRegistry.getByName("player_knockback_sound_pitch", Float.class);
|
EntityPropertyImpl<Float> playerKnockbackSoundPitchProperty = propertyRegistry.getByName("player_knockback_sound_pitch", Float.class);
|
||||||
double lookDistance;
|
double lookDistance;
|
||||||
|
boolean lookReturn;
|
||||||
boolean permissionRequired;
|
boolean permissionRequired;
|
||||||
boolean playerKnockback;
|
boolean playerKnockback;
|
||||||
String playerKnockbackExemptPermission = null;
|
String playerKnockbackExemptPermission = null;
|
||||||
|
@ -64,6 +66,7 @@ public class NpcProcessorTask extends BukkitRunnable {
|
||||||
Player closest = null;
|
Player closest = null;
|
||||||
LookType lookType = npc.getProperty(lookProperty);
|
LookType lookType = npc.getProperty(lookProperty);
|
||||||
lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty));
|
lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty));
|
||||||
|
lookReturn = npc.getProperty(lookReturnProperty);
|
||||||
permissionRequired = npc.getProperty(permissionRequiredProperty);
|
permissionRequired = npc.getProperty(permissionRequiredProperty);
|
||||||
playerKnockback = npc.getProperty(playerKnockbackProperty);
|
playerKnockback = npc.getProperty(playerKnockbackProperty);
|
||||||
if (playerKnockback) {
|
if (playerKnockback) {
|
||||||
|
@ -106,9 +109,13 @@ public class NpcProcessorTask extends BukkitRunnable {
|
||||||
closestDist = distance;
|
closestDist = distance;
|
||||||
closest = player;
|
closest = player;
|
||||||
}
|
}
|
||||||
if (lookType.equals(LookType.PER_PLAYER) && lookDistance >= distance) {
|
if (lookType.equals(LookType.PER_PLAYER)) {
|
||||||
|
if (lookDistance >= distance) {
|
||||||
NpcLocation expected = npc.getLocation().lookingAt(player.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
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());
|
npc.setHeadRotation(player, expected.getYaw(), expected.getPitch());
|
||||||
|
} else if (lookReturn) {
|
||||||
|
npc.setHeadRotation(player, npc.getLocation().getYaw(), npc.getLocation().getPitch());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// player knockback
|
// player knockback
|
||||||
|
@ -132,7 +139,11 @@ public class NpcProcessorTask extends BukkitRunnable {
|
||||||
if (closest != null && lookDistance >= closestDist) {
|
if (closest != null && lookDistance >= closestDist) {
|
||||||
NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
|
||||||
if (!expected.equals(npc.getLocation())) npc.setHeadRotation(expected.getYaw(), expected.getPitch());
|
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.Reference;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public abstract class Viewable {
|
public abstract class Viewable {
|
||||||
|
@ -21,38 +20,74 @@ public abstract class Viewable {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean queueRunning = false;
|
||||||
|
private final Queue<Runnable> visibilityTaskQueue = new ConcurrentLinkedQueue<>();
|
||||||
private final Set<Player> viewers = ConcurrentHashMap.newKeySet();
|
private final Set<Player> viewers = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
public Viewable() {
|
public Viewable() {
|
||||||
all.add(new WeakReference<>(this));
|
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() {
|
public void delete() {
|
||||||
|
queueVisibilityTask(() -> {
|
||||||
UNSAFE_hideAll();
|
UNSAFE_hideAll();
|
||||||
viewers.clear();
|
viewers.clear();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void respawn() {
|
public CompletableFuture<Void> respawn() {
|
||||||
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
queueVisibilityTask(() -> {
|
||||||
UNSAFE_hideAll();
|
UNSAFE_hideAll();
|
||||||
UNSAFE_showAll();
|
UNSAFE_showAll().join();
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void respawn(Player player) {
|
public CompletableFuture<Void> respawn(Player player) {
|
||||||
if (!viewers.contains(player)) return;
|
hide(player);
|
||||||
UNSAFE_hide(player);
|
return show(player);
|
||||||
UNSAFE_show(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show(Player player) {
|
public CompletableFuture<Void> show(Player player) {
|
||||||
if (viewers.contains(player)) return;
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
queueVisibilityTask(() -> {
|
||||||
|
if (viewers.contains(player)) {
|
||||||
|
future.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
viewers.add(player);
|
viewers.add(player);
|
||||||
UNSAFE_show(player);
|
UNSAFE_show(player).join();
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hide(Player player) {
|
public void hide(Player player) {
|
||||||
|
queueVisibilityTask(() -> {
|
||||||
if (!viewers.contains(player)) return;
|
if (!viewers.contains(player)) return;
|
||||||
viewers.remove(player);
|
viewers.remove(player);
|
||||||
UNSAFE_hide(player);
|
UNSAFE_hide(player);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UNSAFE_removeViewer(Player player) {
|
public void UNSAFE_removeViewer(Player player) {
|
||||||
|
@ -63,8 +98,10 @@ public abstract class Viewable {
|
||||||
for (Player viewer : viewers) UNSAFE_hide(viewer);
|
for (Player viewer : viewers) UNSAFE_hide(viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UNSAFE_showAll() {
|
protected CompletableFuture<Void> UNSAFE_showAll() {
|
||||||
for (Player viewer : viewers) UNSAFE_show(viewer);
|
return FutureUtil.allOf(viewers.stream()
|
||||||
|
.map(this::UNSAFE_show)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Player> getViewers() {
|
public Set<Player> getViewers() {
|
||||||
|
@ -75,7 +112,7 @@ public abstract class Viewable {
|
||||||
return viewers.contains(player);
|
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);
|
protected abstract void UNSAFE_hide(Player player);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ api-version: 1.13
|
||||||
|
|
||||||
folia-supported: true
|
folia-supported: true
|
||||||
|
|
||||||
|
depend:
|
||||||
|
- packetevents
|
||||||
|
|
||||||
softdepend:
|
softdepend:
|
||||||
- PlaceholderAPI
|
- PlaceholderAPI
|
||||||
- ServersNPC
|
- ServersNPC
|
||||||
|
|
Loading…
Reference in a new issue