Merge remote-tracking branch 'upstream/2.X' into 2.X
# Conflicts: # build.gradle # gradle/wrapper/gradle-wrapper.properties # plugin/build.gradle
This commit is contained in:
commit
1c927c6b32
86 changed files with 1577 additions and 382 deletions
|
@ -12,14 +12,14 @@ Looking for up-to-date builds of the plugin? Check out our [Jenkins](https://ci.
|
||||||
## Why is it so good?
|
## Why is it so good?
|
||||||
- 100% Packet Based - Nothing is ran on the main thread
|
- 100% Packet Based - Nothing is ran on the main thread
|
||||||
- Performance & stability oriented code
|
- Performance & stability oriented code
|
||||||
- Support for all versions from 1.8 to 1.20.4
|
- Support for all versions from 1.8 to 1.21.8
|
||||||
- Support for multiple different storage options
|
- Support for multiple different storage options
|
||||||
- Intuitive command system
|
- Intuitive command system
|
||||||
|
|
||||||
### Requirements, Extensions & Supported Software
|
### Requirements, Extensions & Supported Software
|
||||||
Requirements:
|
Requirements:
|
||||||
- Java 8+
|
- Java 8+
|
||||||
- Minecraft 1.8 - 1.21
|
- Minecraft 1.8 - 1.21.8
|
||||||
|
|
||||||
Supported Softwares:
|
Supported Softwares:
|
||||||
- Spigot ([Website](https://www.spigotmc.org/))
|
- Spigot ([Website](https://www.spigotmc.org/))
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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,8 @@ package lol.pyr.znpcsplus.api;
|
||||||
|
|
||||||
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
|
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.plugin.ServicePriority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for the registered entity property registry instance
|
* Provider for the registered entity property registry instance
|
||||||
|
@ -30,10 +32,12 @@ public class NpcPropertyRegistryProvider {
|
||||||
* Internal method used to register the main instance of the plugin as the entity property registry provider
|
* Internal method used to register the main instance of the plugin as the entity property registry provider
|
||||||
* You probably shouldn't call this method under any circumstances
|
* You probably shouldn't call this method under any circumstances
|
||||||
*
|
*
|
||||||
|
* @param plugin Instance of the ZNPCsPlus plugin
|
||||||
* @param api Instance of the ZNPCsPlus entity property registry
|
* @param api Instance of the ZNPCsPlus entity property registry
|
||||||
*/
|
*/
|
||||||
public static void register(EntityPropertyRegistry api) {
|
public static void register(Plugin plugin, EntityPropertyRegistry api) {
|
||||||
NpcPropertyRegistryProvider.registry = api;
|
NpcPropertyRegistryProvider.registry = api;
|
||||||
|
Bukkit.getServicesManager().register(EntityPropertyRegistry.class, registry, plugin, ServicePriority.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,11 +31,25 @@ public interface EntityPropertyRegistry {
|
||||||
*/
|
*/
|
||||||
<T> EntityProperty<T> getByName(String name, Class<T> type);
|
<T> EntityProperty<T> getByName(String name, Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a dummy property that can be used to store unique information per npc<br>
|
||||||
|
* Note: Properties registered this way will be player-modifiable by default
|
||||||
|
*
|
||||||
|
* @param name The name of the new property
|
||||||
|
* @param type The type of the new property
|
||||||
|
* @deprecated Use {@link #registerDummy(String, Class, boolean)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default void registerDummy(String name, Class<?> type) {
|
||||||
|
registerDummy(name, type, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a dummy property that can be used to store unique information per npc
|
* Register a dummy property that can be used to store unique information per npc
|
||||||
*
|
*
|
||||||
* @param name The name of the new property
|
* @param name The name of the new property
|
||||||
* @param type The type of the new property
|
* @param type The type of the new property
|
||||||
|
* @param playerModifiable Whether this property can be modified by players using commands
|
||||||
*/
|
*/
|
||||||
void registerDummy(String name, Class<?> type);
|
void registerDummy(String name, Class<?> type, boolean playerModifiable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,27 @@ 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an NPC entry by its UUID
|
||||||
|
* @param uuid The UUID of the NPC entry
|
||||||
|
*/
|
||||||
|
void delete(UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package lol.pyr.znpcsplus.api.skin;
|
package lol.pyr.znpcsplus.api.skin;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for creating skin descriptors.
|
* Factory for creating skin descriptors.
|
||||||
|
@ -8,8 +9,10 @@ import java.net.URL;
|
||||||
public interface SkinDescriptorFactory {
|
public interface SkinDescriptorFactory {
|
||||||
SkinDescriptor createMirrorDescriptor();
|
SkinDescriptor createMirrorDescriptor();
|
||||||
SkinDescriptor createRefreshingDescriptor(String playerName);
|
SkinDescriptor createRefreshingDescriptor(String playerName);
|
||||||
|
SkinDescriptor createRefreshingDescriptor(UUID playerUUID);
|
||||||
SkinDescriptor createStaticDescriptor(String playerName);
|
SkinDescriptor createStaticDescriptor(String playerName);
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
11
api/src/main/java/lol/pyr/znpcsplus/util/ZombieType.java
Normal file
11
api/src/main/java/lol/pyr/znpcsplus/util/ZombieType.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package lol.pyr.znpcsplus.util;
|
||||||
|
|
||||||
|
public enum ZombieType {
|
||||||
|
ZOMBIE,
|
||||||
|
FARMER,
|
||||||
|
LIBRARIAN,
|
||||||
|
PRIEST,
|
||||||
|
BLACKSMITH,
|
||||||
|
BUTCHER,
|
||||||
|
HUSK
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ subprojects {
|
||||||
}
|
}
|
||||||
|
|
||||||
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -9,8 +9,9 @@ processResources {
|
||||||
|
|
||||||
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.12.1" // JSON parsing
|
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
|
||||||
implementation "com.github.retrooper:packetevents-spigot:2.7.1-SNAPSHOT" // Packets
|
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
|
||||||
|
implementation "com.github.retrooper:packetevents-spigot:2.9.3" // 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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -92,10 +93,10 @@ 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(bootstrap, propertyRegistry);
|
||||||
shutdownTasks.add(NpcPropertyRegistryProvider::unregister);
|
shutdownTasks.add(NpcPropertyRegistryProvider::unregister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +129,15 @@ public class ZNpcsPlus {
|
||||||
|
|
||||||
|
|
||||||
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
|
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager);
|
||||||
propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer, scheduler);
|
propertyRegistry.registerTypes(packetFactory, textSerializer, scheduler);
|
||||||
|
|
||||||
BungeeConnector bungeeConnector = new BungeeConnector(bootstrap);
|
BungeeConnector bungeeConnector = new BungeeConnector(bootstrap);
|
||||||
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();
|
||||||
|
@ -157,7 +159,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()) {
|
||||||
|
@ -191,7 +193,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("");
|
||||||
|
|
||||||
|
@ -244,7 +246,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);
|
||||||
|
@ -294,6 +296,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))
|
||||||
|
@ -319,7 +322,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))
|
||||||
|
@ -339,6 +342,7 @@ public class ZNpcsPlus {
|
||||||
.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();
|
||||||
|
|
|
@ -11,16 +11,20 @@ import lol.pyr.znpcsplus.npc.NpcImpl;
|
||||||
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.skin.cache.MojangSkinCache;
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.NameFetchingDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
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;
|
||||||
|
@ -63,7 +67,7 @@ public class SkinCommand implements CommandHandler {
|
||||||
} else if (type.equalsIgnoreCase("dynamic")) {
|
} else if (type.equalsIgnoreCase("dynamic")) {
|
||||||
context.ensureArgsNotEmpty();
|
context.ensureArgsNotEmpty();
|
||||||
String name = context.dumpAllArgs();
|
String name = context.dumpAllArgs();
|
||||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, name));
|
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new NameFetchingDescriptor(skinCache, name));
|
||||||
npc.respawn();
|
npc.respawn();
|
||||||
context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\""));
|
context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\""));
|
||||||
} else if (type.equalsIgnoreCase("url")) {
|
} else if (type.equalsIgnoreCase("url")) {
|
||||||
|
@ -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,66 @@
|
||||||
|
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(")");
|
||||||
|
} else {
|
||||||
|
versionBuilder.append(" (Development Build)");
|
||||||
|
}
|
||||||
|
|
||||||
|
String version = versionBuilder.toString();
|
||||||
|
|
||||||
|
context.send(Component.text(version, NamedTextColor.GREEN)
|
||||||
|
.hoverEvent(Component.text("Click to copy version to clipboard"))
|
||||||
|
.clickEvent(ClickEvent.copyToClipboard(version)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
@ -44,6 +45,7 @@ public class PropertySetCommand implements CommandHandler {
|
||||||
// TODO: find a way to do this better & rewrite this mess
|
// TODO: find a way to do this better & rewrite this mess
|
||||||
|
|
||||||
if (!npc.getType().getAllowedProperties().contains(property)) context.halt(Component.text("Property " + property.getName() + " not allowed for npc type " + npc.getType().getName(), NamedTextColor.RED));
|
if (!npc.getType().getAllowedProperties().contains(property)) context.halt(Component.text("Property " + property.getName() + " not allowed for npc type " + npc.getType().getName(), NamedTextColor.RED));
|
||||||
|
if (!property.isPlayerModifiable()) context.halt(Component.text("This property is not modifiable by players", NamedTextColor.RED));
|
||||||
Class<?> type = property.getType();
|
Class<?> type = property.getType();
|
||||||
Object value;
|
Object value;
|
||||||
String valueName;
|
String valueName;
|
||||||
|
@ -124,6 +126,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));
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class HologramTrait extends SectionCitizensTrait {
|
||||||
if (linesSection != null) {
|
if (linesSection != null) {
|
||||||
List<String> keys = new ArrayList<>(linesSection.getKeys(false));
|
List<String> keys = new ArrayList<>(linesSection.getKeys(false));
|
||||||
for (int i = keys.size() - 1; i >= 0; i--) {
|
for (int i = keys.size() - 1; i >= 0; i--) {
|
||||||
String line = linesSection.getConfigurationSection(keys.get(i)).getString("text");
|
String line = linesSection.isConfigurationSection(keys.get(i)) ? linesSection.getConfigurationSection(keys.get(i)).getString("text") : linesSection.getString(keys.get(i));
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
Component component = textSerializer.deserialize(line);
|
Component component = textSerializer.deserialize(line);
|
||||||
npc.getHologram().addTextLineComponent(component);
|
npc.getHologram().addTextLineComponent(component);
|
||||||
|
|
|
@ -25,7 +25,7 @@ import lol.pyr.znpcsplus.packets.PacketFactory;
|
||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||||
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.skin.descriptor.FetchingDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.NameFetchingDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
||||||
import lol.pyr.znpcsplus.util.BungeeConnector;
|
import lol.pyr.znpcsplus.util.BungeeConnector;
|
||||||
|
@ -175,7 +175,7 @@ public class ZNpcImporter implements DataImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.getSkinName() != null) {
|
if (model.getSkinName() != null) {
|
||||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, model.getSkinName()));
|
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new NameFetchingDescriptor(skinCache, model.getSkinName()));
|
||||||
}
|
}
|
||||||
else if (model.getSkin() != null && model.getSignature() != null) {
|
else if (model.getSkin() != null && model.getSignature() != null) {
|
||||||
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new PrefetchedDescriptor(new SkinImpl(model.getSkin(), model.getSignature())));
|
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new PrefetchedDescriptor(new SkinImpl(model.getSkin(), model.getSignature())));
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,16 +47,16 @@ public abstract class EntityPropertyImpl<T> implements EntityProperty<T> {
|
||||||
dependencies.add(property);
|
dependencies.add(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static <V> EntityData newEntityData(int index, EntityDataType<V> type, V value) {
|
protected static <V> EntityData<V> newEntityData(int index, EntityDataType<V> type, V value) {
|
||||||
return new EntityData(index, type, value);
|
return new EntityData<>(index, type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
|
public List<EntityData<?>> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
|
||||||
Map<Integer, EntityData> map = new HashMap<>();
|
Map<Integer, EntityData<?>> map = new HashMap<>();
|
||||||
apply(player, packetEntity, isSpawned, map);
|
apply(player, packetEntity, isSpawned, map);
|
||||||
for (EntityPropertyImpl<?> property : dependencies) property.apply(player, packetEntity, isSpawned, map);
|
for (EntityPropertyImpl<?> property : dependencies) property.apply(player, packetEntity, isSpawned, map);
|
||||||
return new ArrayList<>(map.values());
|
return new ArrayList<>(map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties);
|
abstract public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties);
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
@ -9,12 +10,12 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
|
||||||
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
|
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
|
||||||
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
|
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
|
||||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||||
import lol.pyr.znpcsplus.ZNpcsPlusBootstrap;
|
|
||||||
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
import lol.pyr.znpcsplus.api.entity.EntityProperty;
|
||||||
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
|
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 +33,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 +92,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);
|
||||||
|
|
||||||
|
@ -105,7 +108,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerTypes(ZNpcsPlusBootstrap plugin, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, TaskScheduler taskScheduler) {
|
public void registerTypes(PacketFactory packetFactory, LegacyComponentSerializer textSerializer, TaskScheduler taskScheduler) {
|
||||||
ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
|
ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
|
||||||
boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
|
boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
|
||||||
boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9);
|
boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9);
|
||||||
|
@ -124,11 +127,12 @@ 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));
|
||||||
|
|
||||||
register(new ForceBodyRotationProperty(plugin, taskScheduler));
|
register(new ForceBodyRotationProperty(taskScheduler));
|
||||||
|
|
||||||
register(new DummyProperty<>("player_knockback", false));
|
register(new DummyProperty<>("player_knockback", false));
|
||||||
register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
|
register(new DummyProperty<>("player_knockback_exempt_permission", String.class));
|
||||||
|
@ -151,6 +155,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;
|
||||||
|
@ -177,11 +191,13 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) babyIndex = 11;
|
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) babyIndex = 11;
|
||||||
else babyIndex = 12;
|
else babyIndex = 12;
|
||||||
if (ver.isOlderThan(ServerVersion.V_1_9)) {
|
if (ver.isOlderThan(ServerVersion.V_1_9)) {
|
||||||
register(new EncodedByteProperty<>("baby", false, babyIndex, obj -> (byte) (obj ? -1 : 0)));
|
register(new LegacyBabyProperty(babyIndex));
|
||||||
} else {
|
} else {
|
||||||
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;
|
||||||
|
@ -269,7 +285,9 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
else horseIndex = 16;
|
else horseIndex = 16;
|
||||||
int horseEating = ver.isNewerThanOrEquals(ServerVersion.V_1_12) ? 0x10 : 0x20;
|
int horseEating = ver.isNewerThanOrEquals(ServerVersion.V_1_12) ? 0x10 : 0x20;
|
||||||
register(new BitsetProperty("is_tame", horseIndex, 0x02, false, legacyBooleans));
|
register(new BitsetProperty("is_tame", horseIndex, 0x02, false, legacyBooleans));
|
||||||
|
if (ver.isOlderThan(ServerVersion.V_1_21)) {
|
||||||
register(new BitsetProperty("is_saddled", horseIndex, 0x04, false, legacyBooleans));
|
register(new BitsetProperty("is_saddled", horseIndex, 0x04, false, legacyBooleans));
|
||||||
|
}
|
||||||
register(new BitsetProperty("is_eating", horseIndex, horseEating, false, legacyBooleans));
|
register(new BitsetProperty("is_eating", horseIndex, horseEating, false, legacyBooleans));
|
||||||
register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans));
|
register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans));
|
||||||
register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans));
|
register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans));
|
||||||
|
@ -337,10 +355,14 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
// Chested Horse
|
// Chested Horse
|
||||||
if (ver.isOlderThan(ServerVersion.V_1_11)) {
|
if (ver.isOlderThan(ServerVersion.V_1_11)) {
|
||||||
register(new BitsetProperty("has_chest", horseIndex, 0x08, false, legacyBooleans));
|
register(new BitsetProperty("has_chest", horseIndex, 0x08, false, legacyBooleans));
|
||||||
linkProperties("is_saddled", "has_chest", "is_eating", "is_rearing", "has_mouth_open");
|
linkProperties("is_tame", "is_saddled", "has_chest", "is_eating", "is_rearing", "has_mouth_open");
|
||||||
} else {
|
} else {
|
||||||
register(new BooleanProperty("has_chest", horseVariantIndex, false, legacyBooleans));
|
register(new BooleanProperty("has_chest", horseVariantIndex, false, legacyBooleans));
|
||||||
linkProperties("is_saddled", "is_eating", "is_rearing", "has_mouth_open");
|
if (ver.isOlderThan(ServerVersion.V_1_21)){
|
||||||
|
linkProperties("is_tame", "is_saddled", "is_eating", "is_rearing", "has_mouth_open");
|
||||||
|
} else {
|
||||||
|
linkProperties("is_tame", "is_eating", "is_rearing", "has_mouth_open");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slime, Magma Cube and Phantom
|
// Slime, Magma Cube and Phantom
|
||||||
|
@ -436,6 +458,39 @@ 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zombie
|
||||||
|
int zombieIndex;
|
||||||
|
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) zombieIndex = 17;
|
||||||
|
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) zombieIndex = 16;
|
||||||
|
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) zombieIndex = 13;
|
||||||
|
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) zombieIndex = 12;
|
||||||
|
else zombieIndex = 13;
|
||||||
|
|
||||||
|
if (ver.isOlderThan(ServerVersion.V_1_9)) {
|
||||||
|
register(new EncodedByteProperty<>("zombie_is_villager", false, zombieIndex++, b -> (byte) (b ? 1 : 0)));
|
||||||
|
} else if (ver.isOlderThan(ServerVersion.V_1_11)) {
|
||||||
|
register(new EncodedIntegerProperty<>("zombie_type", ZombieType.ZOMBIE, zombieIndex++, Enum::ordinal));
|
||||||
|
} else {
|
||||||
|
zombieIndex++; // Not a mistake, this is field unused in 1.11+
|
||||||
|
}
|
||||||
|
if (ver.isOlderThan(ServerVersion.V_1_9)) {
|
||||||
|
register(new EncodedByteProperty<>("is_converting", false, zombieIndex++, b -> (byte) (b ? 1 : 0)));
|
||||||
|
} else if (ver.isOlderThan(ServerVersion.V_1_11)) {
|
||||||
|
register(new BooleanProperty("is_converting", zombieIndex++, false, legacyBooleans));
|
||||||
|
}
|
||||||
|
if (ver.isNewerThanOrEquals(ServerVersion.V_1_9) && ver.isOlderThan(ServerVersion.V_1_14)) {
|
||||||
|
register(new BooleanProperty("zombie_hands_held_up", zombieIndex++, false, legacyBooleans));
|
||||||
|
}
|
||||||
|
if (ver.isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||||
|
register(new BooleanProperty("zombie_becoming_drowned", zombieIndex++, false, legacyBooleans));
|
||||||
|
}
|
||||||
|
|
||||||
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return;
|
||||||
// Shulker
|
// Shulker
|
||||||
int shulkerIndex;
|
int shulkerIndex;
|
||||||
|
@ -483,7 +538,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 +720,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) {
|
||||||
|
@ -721,8 +790,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerDummy(String name, Class<?> type) {
|
public void registerDummy(String name, Class<?> type, boolean playerModifiable) {
|
||||||
register(new DummyProperty<>(name, type));
|
register(new DummyProperty<>(name, type, playerModifiable));
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityPropertyImpl<?> getByName(String name) {
|
public EntityPropertyImpl<?> getByName(String name) {
|
||||||
|
|
|
@ -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,120 @@ 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 (vehicle != null) {
|
||||||
|
setVehicle(vehicle);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
|
|
@ -31,13 +31,22 @@ public class BitsetProperty extends EntityPropertyImpl<Boolean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
EntityData oldData = properties.get(index);
|
EntityData<?> oldData = properties.get(index);
|
||||||
boolean enabled = entity.getProperty(this);
|
boolean enabled = entity.getProperty(this);
|
||||||
if (inverted) enabled = !enabled;
|
if (inverted) enabled = !enabled;
|
||||||
properties.put(index,
|
if (integer) {
|
||||||
integer ? newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : (int) oldData.getValue()) | (enabled ? bitmask : 0)) :
|
int oldValue = 0;
|
||||||
newEntityData(index, EntityDataTypes.BYTE, (byte) ((oldData == null ? 0 : (byte) oldData.getValue()) | (enabled ? bitmask : 0))));
|
if (oldData != null && oldData.getValue() instanceof Number) {
|
||||||
|
oldValue = ((Number) oldData.getValue()).intValue();
|
||||||
|
}
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.INT, oldValue | (enabled ? bitmask : 0)));
|
||||||
|
} else {
|
||||||
|
byte oldValue = 0;
|
||||||
|
if (oldData != null && oldData.getValue() instanceof Number) {
|
||||||
|
oldValue = ((Number) oldData.getValue()).byteValue();
|
||||||
|
}
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (oldValue | (enabled ? bitmask : 0))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class BooleanProperty extends EntityPropertyImpl<Boolean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
boolean enabled = entity.getProperty(this);
|
boolean enabled = entity.getProperty(this);
|
||||||
if (inverted) enabled = !enabled;
|
if (inverted) enabled = !enabled;
|
||||||
if (legacy) properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (enabled ? 1 : 0)));
|
if (legacy) properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (enabled ? 1 : 0)));
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class CamelSittingProperty extends EntityPropertyImpl<Boolean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
boolean value = entity.getProperty(this);
|
boolean value = entity.getProperty(this);
|
||||||
if (value) {
|
if (value) {
|
||||||
properties.put(poseIndex, newEntityData(poseIndex, EntityDataTypes.ENTITY_POSE, EntityPose.SITTING));
|
properties.put(poseIndex, newEntityData(poseIndex, EntityDataTypes.ENTITY_POSE, EntityPose.SITTING));
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class CustomTypeProperty<T, U> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
properties.put(index, newEntityData(index, type, decoder.decode(entity.getProperty(this))));
|
properties.put(index, newEntityData(index, type, decoder.decode(entity.getProperty(this))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package lol.pyr.znpcsplus.entity.properties;
|
package lol.pyr.znpcsplus.entity.properties;
|
||||||
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||||
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
|
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||||
|
@ -16,20 +15,21 @@ import java.util.Optional;
|
||||||
public class DinnerboneProperty extends EntityPropertyImpl<Boolean> {
|
public class DinnerboneProperty extends EntityPropertyImpl<Boolean> {
|
||||||
private final boolean optional;
|
private final boolean optional;
|
||||||
private final Object serialized;
|
private final Object serialized;
|
||||||
private final EntityDataType<?> type;
|
|
||||||
|
|
||||||
public DinnerboneProperty(boolean legacy, boolean optional) {
|
public DinnerboneProperty(boolean legacy, boolean optional) {
|
||||||
super("dinnerbone", false, Boolean.class);
|
super("dinnerbone", false, Boolean.class);
|
||||||
this.optional = optional;
|
this.optional = optional;
|
||||||
Component name = Component.text("Dinnerbone");
|
Component name = Component.text("Dinnerbone");
|
||||||
Object serialized = legacy ? AdventureSerializer.getLegacyGsonSerializer().serialize(name) :
|
this.serialized = legacy ? AdventureSerializer.serializer().legacy().serialize(name) :
|
||||||
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
|
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
|
||||||
this.serialized = optional ? Optional.of(serialized) : serialized;
|
|
||||||
this.type = optional ? EntityDataTypes.OPTIONAL_ADV_COMPONENT : EntityDataTypes.STRING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
properties.put(2, new EntityData(2, type, entity.getProperty(this) ? serialized : optional ? null : ""));
|
if (optional) {
|
||||||
|
properties.put(2, new EntityData<>(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, entity.getProperty(this) ? Optional.of((Component) serialized) : Optional.empty()));
|
||||||
|
} else {
|
||||||
|
properties.put(2, new EntityData<>(2, EntityDataTypes.STRING, entity.getProperty(this) ? (String) serialized : ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,6 @@ public class DummyProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class EncodedByteProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
T value = entity.getProperty(this);
|
T value = entity.getProperty(this);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class EncodedIntegerProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
T value = entity.getProperty(this);
|
T value = entity.getProperty(this);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class EncodedStringProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
T value = entity.getProperty(this);
|
T value = entity.getProperty(this);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ public class EquipmentProperty extends EntityPropertyImpl<ItemStack> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
packetFactory.sendEquipment(player, entity, new Equipment(slot, entity.getProperty(this)));
|
packetFactory.sendEquipment(player, entity, new Equipment(slot, entity.getProperty(this)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package lol.pyr.znpcsplus.entity.properties;
|
package lol.pyr.znpcsplus.entity.properties;
|
||||||
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||||
import lol.pyr.znpcsplus.ZNpcsPlusBootstrap;
|
|
||||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
@ -9,17 +8,15 @@ import org.bukkit.entity.Player;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ForceBodyRotationProperty extends DummyProperty<Boolean> {
|
public class ForceBodyRotationProperty extends DummyProperty<Boolean> {
|
||||||
private final ZNpcsPlusBootstrap plugin;
|
|
||||||
private final TaskScheduler scheduler;
|
private final TaskScheduler scheduler;
|
||||||
|
|
||||||
public ForceBodyRotationProperty(ZNpcsPlusBootstrap plugin, TaskScheduler scheduler) {
|
public ForceBodyRotationProperty(TaskScheduler scheduler) {
|
||||||
super("force_body_rotation", false);
|
super("force_body_rotation", false);
|
||||||
this.plugin = plugin;
|
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
if (entity.getProperty(this)) {
|
if (entity.getProperty(this)) {
|
||||||
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 2L);
|
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 2L);
|
||||||
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 6L);
|
scheduler.runLaterAsync(() -> entity.swingHand(player, false), 6L);
|
||||||
|
|
|
@ -19,12 +19,19 @@ public class GlowProperty extends EntityPropertyImpl<NamedColor> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
NamedColor value = entity.getProperty(this);
|
NamedColor value = entity.getProperty(this);
|
||||||
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();
|
||||||
|
byte oldValue = 0;
|
||||||
|
if (oldData != null && oldData.getValue() instanceof Number) {
|
||||||
|
oldValue = ((Number) oldData.getValue()).byteValue();
|
||||||
|
}
|
||||||
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));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ public class HologramItemProperty extends EntityPropertyImpl<ItemStack> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
properties.put(8, newEntityData(8, EntityDataTypes.ITEMSTACK, entity.getProperty(this)));
|
properties.put(8, newEntityData(8, EntityDataTypes.ITEMSTACK, entity.getProperty(this)));
|
||||||
properties.put(5, newEntityData(5, EntityDataTypes.BOOLEAN, true));
|
properties.put(5, newEntityData(5, EntityDataTypes.BOOLEAN, true));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,13 @@ public class HorseColorProperty extends EntityPropertyImpl<HorseColor> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
EntityData oldData = properties.get(index);
|
EntityData<?> oldData = properties.get(index);
|
||||||
HorseColor value = entity.getProperty(this);
|
HorseColor value = entity.getProperty(this);
|
||||||
properties.put(index, newEntityData(index, EntityDataTypes.INT, value.ordinal() | (oldData == null ? 0 : ((int) oldData.getValue() & 0xFF00))));
|
int oldValue = (oldData != null && oldData.getValue() instanceof Integer) ? (Integer) oldData.getValue() : 0;
|
||||||
|
|
||||||
|
int newValue = value.ordinal() | (oldValue & 0xFF00);
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.INT, newValue));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,13 @@ public class HorseStyleProperty extends EntityPropertyImpl<HorseStyle> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
EntityData oldData = properties.get(index);
|
EntityData<?> oldData = properties.get(index);
|
||||||
HorseStyle value = entity.getProperty(this);
|
HorseStyle value = entity.getProperty(this);
|
||||||
properties.put(index, newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : ((int) oldData.getValue() & 0x00FF)) | (value.ordinal() << 8)));
|
|
||||||
|
int oldValue = (oldData != null && oldData.getValue() instanceof Integer) ? (Integer) oldData.getValue() : 0;
|
||||||
|
int newValue = (oldValue & 0x00FF) | (value.ordinal() << 8);
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.INT, newValue));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class IntegerProperty extends EntityPropertyImpl<Integer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
properties.put(index, legacy ?
|
properties.put(index, legacy ?
|
||||||
newEntityData(index, EntityDataTypes.BYTE, (byte) entity.getProperty(this).intValue()) :
|
newEntityData(index, EntityDataTypes.BYTE, (byte) entity.getProperty(this).intValue()) :
|
||||||
newEntityData(index, EntityDataTypes.INT, entity.getProperty(this)));
|
newEntityData(index, EntityDataTypes.INT, entity.getProperty(this)));
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package lol.pyr.znpcsplus.entity.properties;
|
||||||
|
|
||||||
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||||
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||||
|
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||||
|
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||||
|
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class LegacyBabyProperty extends EntityPropertyImpl<Boolean> {
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
public LegacyBabyProperty(int index) {
|
||||||
|
super("baby", false, Boolean.class);
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
|
boolean isBaby = entity.getProperty(this);
|
||||||
|
if (entity.getType().equals(EntityTypes.ZOMBIE)) {
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (isBaby ? 1 : 0)));
|
||||||
|
} else {
|
||||||
|
properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (isBaby ? -1 : 0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ public class NBTProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
T value = entity.getProperty(this);
|
T value = entity.getProperty(this);
|
||||||
if (value == null && !allowNull) return;
|
if (value == null && !allowNull) return;
|
||||||
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
properties.put(index, newEntityData(index, type, decoder.decode(value)));
|
||||||
|
|
|
@ -27,14 +27,17 @@ public class NameProperty extends EntityPropertyImpl<Component> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
Component value = entity.getProperty(this);
|
Component value = entity.getProperty(this);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
value = PapiUtil.set(legacySerializer, player, value);
|
value = PapiUtil.set(legacySerializer, player, value);
|
||||||
Object serialized = legacySerialization ? AdventureSerializer.getLegacyGsonSerializer().serialize(value) :
|
if (legacySerialization) {
|
||||||
optional ? value : LegacyComponentSerializer.legacySection().serialize(value);
|
properties.put(2, newEntityData(2, EntityDataTypes.STRING, AdventureSerializer.serializer().asJson(value)));
|
||||||
if (optional) properties.put(2, new EntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of(serialized)));
|
} else if (optional) {
|
||||||
else properties.put(2, new EntityData(2, EntityDataTypes.STRING, serialized));
|
properties.put(2, newEntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of(value)));
|
||||||
|
} else {
|
||||||
|
properties.put(2, newEntityData(2, EntityDataTypes.STRING, LegacyComponentSerializer.legacySection().serialize(value)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (legacySerialization) properties.put(3, newEntityData(3, EntityDataTypes.BYTE, (byte) (value != null ? 1 : 0)));
|
if (legacySerialization) properties.put(3, newEntityData(3, EntityDataTypes.BYTE, (byte) (value != null ? 1 : 0)));
|
||||||
|
|
|
@ -19,10 +19,10 @@ public class OptionalBlockPosProperty extends EntityPropertyImpl<Vector3i> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
Vector3i value = entity.getProperty(this);
|
Vector3i value = entity.getProperty(this);
|
||||||
if (value == null) properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty()));
|
if (value == null) properties.put(index, new EntityData<>(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty()));
|
||||||
else properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION,
|
else properties.put(index, new EntityData<>(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION,
|
||||||
Optional.of(new com.github.retrooper.packetevents.util.Vector3i(value.getX(), value.getY(), value.getZ()))));
|
Optional.of(new com.github.retrooper.packetevents.util.Vector3i(value.getX(), value.getY(), value.getZ()))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package lol.pyr.znpcsplus.entity.properties;
|
package lol.pyr.znpcsplus.entity.properties;
|
||||||
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||||
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
|
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
|
||||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||||
|
@ -17,31 +16,38 @@ import java.util.Optional;
|
||||||
public class RabbitTypeProperty extends EntityPropertyImpl<RabbitType> {
|
public class RabbitTypeProperty extends EntityPropertyImpl<RabbitType> {
|
||||||
private final int index;
|
private final int index;
|
||||||
private final boolean legacyBooleans;
|
private final boolean legacyBooleans;
|
||||||
|
private final boolean optional;
|
||||||
private final Object serialized;
|
private final Object serialized;
|
||||||
private final EntityDataType<?> type;
|
|
||||||
|
|
||||||
public RabbitTypeProperty(int index, boolean legacyBooleans, boolean legacyNames, boolean optional) {
|
public RabbitTypeProperty(int index, boolean legacyBooleans, boolean legacyNames, boolean optional) {
|
||||||
super("rabbit_type", RabbitType.BROWN, RabbitType.class);
|
super("rabbit_type", RabbitType.BROWN, RabbitType.class);
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.legacyBooleans = legacyBooleans;
|
this.legacyBooleans = legacyBooleans;
|
||||||
|
this.optional = optional;
|
||||||
Component name = Component.text("Toast");
|
Component name = Component.text("Toast");
|
||||||
Object serialized = legacyNames ? AdventureSerializer.getLegacyGsonSerializer().serialize(name) :
|
this.serialized = legacyNames ? AdventureSerializer.serializer().legacy().serialize(name) :
|
||||||
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
|
optional ? name : LegacyComponentSerializer.legacySection().serialize(name);
|
||||||
this.serialized = optional ? Optional.of(serialized) : serialized;
|
|
||||||
this.type = optional ? EntityDataTypes.OPTIONAL_ADV_COMPONENT : EntityDataTypes.STRING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
RabbitType rabbitType = entity.getProperty(this);
|
RabbitType rabbitType = entity.getProperty(this);
|
||||||
if (rabbitType == null) return;
|
if (rabbitType == null) return;
|
||||||
if (!rabbitType.equals(RabbitType.TOAST)) {
|
if (!rabbitType.equals(RabbitType.TOAST)) {
|
||||||
properties.put(index, legacyBooleans ?
|
properties.put(index, legacyBooleans ?
|
||||||
newEntityData(index, EntityDataTypes.BYTE, (byte) rabbitType.getId()) :
|
newEntityData(index, EntityDataTypes.BYTE, (byte) rabbitType.getId()) :
|
||||||
newEntityData(index, EntityDataTypes.INT, rabbitType.getId()));
|
newEntityData(index, EntityDataTypes.INT, rabbitType.getId()));
|
||||||
properties.put(2, new EntityData(2, type, null));
|
if (optional) {
|
||||||
|
properties.put(2, new EntityData<>(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.empty()));
|
||||||
} else {
|
} else {
|
||||||
properties.put(2, new EntityData(2, type, serialized));
|
properties.put(2, new EntityData<>(2, EntityDataTypes.STRING, ""));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (optional) {
|
||||||
|
properties.put(2, newEntityData(2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.of((Component) serialized)));
|
||||||
|
} else {
|
||||||
|
properties.put(2, newEntityData(2, EntityDataTypes.STRING, (String) serialized));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class RotationProperty extends EntityPropertyImpl<Vector3f> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
Vector3f vec = entity.getProperty(this);
|
Vector3f vec = entity.getProperty(this);
|
||||||
properties.put(index, newEntityData(index, EntityDataTypes.ROTATION, new com.github.retrooper.packetevents.util.Vector3f(vec.getX(), vec.getY(), vec.getZ())));
|
properties.put(index, newEntityData(index, EntityDataTypes.ROTATION, new com.github.retrooper.packetevents.util.Vector3f(vec.getX(), vec.getY(), vec.getZ())));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class TargetNpcProperty extends EntityPropertyImpl<NpcEntryImpl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
NpcEntryImpl value = entity.getProperty(this);
|
NpcEntryImpl value = entity.getProperty(this);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
if (value.getNpc().getEntity().getEntityId() == entity.getEntityId()) return;
|
if (value.getNpc().getEntity().getEntityId() == entity.getEntityId()) return;
|
||||||
|
|
|
@ -25,15 +25,14 @@ public class TropicalFishVariantProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
T value = entity.getProperty(this);
|
T value = entity.getProperty(this);
|
||||||
if (value == null) {
|
if (value == null) return;
|
||||||
return;
|
|
||||||
}
|
EntityData<?> oldData = properties.get(index);
|
||||||
EntityData oldData = properties.get(index);
|
|
||||||
TropicalFishVariant.Builder builder;
|
TropicalFishVariant.Builder builder;
|
||||||
if (oldData != null && oldData.getType() == EntityDataTypes.INT && oldData.getValue() != null) {
|
if (oldData != null && oldData.getType() == EntityDataTypes.INT && oldData.getValue() instanceof Integer) {
|
||||||
int oldVal = (int) oldData.getValue();
|
int oldVal = (Integer) oldData.getValue();
|
||||||
builder = TropicalFishVariant.Builder.fromInt(oldVal);
|
builder = TropicalFishVariant.Builder.fromInt(oldVal);
|
||||||
} else {
|
} else {
|
||||||
builder = new TropicalFishVariant.Builder();
|
builder = new TropicalFishVariant.Builder();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,8 +21,8 @@ public abstract class VillagerDataProperty<T> extends EntityPropertyImpl<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
|
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData<?>> properties) {
|
||||||
EntityData oldData = properties.get(index);
|
EntityData<?> oldData = properties.get(index);
|
||||||
VillagerData old = oldData == null ? new VillagerData(VillagerTypes.PLAINS, VillagerProfessions.NONE, 1) : (VillagerData) oldData.getValue();
|
VillagerData old = oldData == null ? new VillagerData(VillagerTypes.PLAINS, VillagerProfessions.NONE, 1) : (VillagerData) oldData.getValue();
|
||||||
properties.put(index, newEntityData(index, EntityDataTypes.VILLAGER_DATA, apply(old, entity.getProperty(this))));
|
properties.put(index, newEntityData(index, EntityDataTypes.VILLAGER_DATA, apply(old, entity.getProperty(this))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,14 +41,15 @@ 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTextLine(String line) {
|
public void addTextLine(String line) {
|
||||||
addTextLineComponent(textSerializer.deserialize(textSerializer.serialize(MiniMessage.miniMessage().deserialize(line))));
|
Component component = line.contains("§") ? Component.text(line) : MiniMessage.miniMessage().deserialize(line);
|
||||||
|
addTextLineComponent(textSerializer.deserialize(textSerializer.serialize(component)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItemLineStack(org.bukkit.inventory.ItemStack item) {
|
public void addItemLineStack(org.bukkit.inventory.ItemStack item) {
|
||||||
|
@ -57,9 +61,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,9 +109,9 @@ public class HologramImpl extends Viewable implements Hologram {
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,9 +124,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +148,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
|
||||||
|
@ -178,14 +184,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")
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +159,7 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
|
private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
|
||||||
if (!type.isAllowedProperty(property)) return;
|
if (!type.isAllowedProperty(property)) return;
|
||||||
for (Player viewer : getViewers()) {
|
for (Player viewer : getViewers()) {
|
||||||
List<EntityData> data = property.applyStandalone(viewer, entity, true);
|
List<EntityData<?>> data = property.applyStandalone(viewer, entity, true);
|
||||||
if (!data.isEmpty()) packetFactory.sendMetadata(viewer, entity, data);
|
if (!data.isEmpty()) packetFactory.sendMetadata(viewer, entity, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +232,8 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAction(InteractionAction action) {
|
public void addAction(InteractionAction action) throws IllegalArgumentException {
|
||||||
|
if (action == null) throw new IllegalArgumentException("action can not be null");
|
||||||
actions.add(action);
|
actions.add(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +243,8 @@ public class NpcImpl extends Viewable implements Npc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void editAction(int index, InteractionAction action) {
|
public void editAction(int index, InteractionAction action) throws IllegalArgumentException {
|
||||||
|
if (action == null) throw new IllegalArgumentException("action can not be null");
|
||||||
actions.set(index, action);
|
actions.set(index, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,4 +268,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);
|
||||||
|
@ -195,6 +202,14 @@ public class NpcRegistryImpl implements NpcRegistry {
|
||||||
storage.deleteNpc(entry);
|
storage.deleteNpc(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(UUID uuid) {
|
||||||
|
NpcEntryImpl entry = npcUuidLookupMap.get(uuid);
|
||||||
|
if (entry == null) return;
|
||||||
|
unregister(entry);
|
||||||
|
storage.deleteNpc(entry);
|
||||||
|
}
|
||||||
|
|
||||||
public void switchIds(String oldId, String newId) {
|
public void switchIds(String oldId, String newId) {
|
||||||
NpcEntryImpl entry = getById(oldId);
|
NpcEntryImpl entry = getById(oldId);
|
||||||
delete(oldId);
|
delete(oldId);
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -174,6 +180,35 @@ public class NpcTypeImpl implements NpcType {
|
||||||
if (version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
|
if (version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
|
||||||
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.WOLF)) {
|
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.WOLF)) {
|
||||||
addProperties("wolf_variant");
|
addProperties("wolf_variant");
|
||||||
|
if (version.isNewerThanOrEquals(ServerVersion.V_1_21)) {
|
||||||
|
addProperties("body");
|
||||||
|
} else {
|
||||||
|
addProperties("chestplate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (version.isNewerThanOrEquals(ServerVersion.V_1_21_4)) {
|
||||||
|
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.CREAKING)) {
|
||||||
|
addProperties("creaking_crumbling");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ZOMBIE)) {
|
||||||
|
if (version.isOlderThan(ServerVersion.V_1_9)) {
|
||||||
|
addProperties("zombie_is_villager");
|
||||||
|
} else if (version.isOlderThan(ServerVersion.V_1_11)) {
|
||||||
|
addProperties("zombie_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.isOlderThan(ServerVersion.V_1_11)) {
|
||||||
|
addProperties("is_converting");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.isNewerThanOrEquals(ServerVersion.V_1_9) && version.isOlderThan(ServerVersion.V_1_14)) {
|
||||||
|
addProperties("zombie_hands_held_up");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||||
|
addProperties("zombie_becoming_drowned");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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,14 +299,14 @@ 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));
|
||||||
|
|
||||||
register(builder(p, "trader_llama", EntityTypes.TRADER_LLAMA)
|
register(builder(p, "trader_llama", EntityTypes.TRADER_LLAMA)
|
||||||
.setHologramOffset(-0.105)
|
.setHologramOffset(-0.105)
|
||||||
.addProperties("llama_variant"));
|
.addProperties("carpet_color", "llama_variant", "body"));
|
||||||
|
|
||||||
register(builder(p, "wandering_trader", EntityTypes.WANDERING_TRADER)
|
register(builder(p, "wandering_trader", EntityTypes.WANDERING_TRADER)
|
||||||
.setHologramOffset(-0.025)
|
.setHologramOffset(-0.025)
|
||||||
|
@ -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)
|
||||||
|
@ -383,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);
|
||||||
|
@ -21,7 +22,10 @@ public interface PacketFactory {
|
||||||
void removeTeam(Player player, PacketEntity entity);
|
void removeTeam(Player player, PacketEntity entity);
|
||||||
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
|
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
|
||||||
void sendEquipment(Player player, PacketEntity entity, Equipment equipment);
|
void sendEquipment(Player player, PacketEntity entity, Equipment equipment);
|
||||||
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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +135,13 @@ public class V1_8PacketFactory implements PacketFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
|
public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||||
Map<Integer, EntityData> datas = new HashMap<>();
|
Map<Integer, EntityData<?>> datas = new HashMap<>();
|
||||||
for (EntityProperty<?> property : properties.getAppliedProperties()) ((EntityPropertyImpl<?>) property).apply(player, entity, false, datas);
|
for (EntityProperty<?> property : properties.getAppliedProperties()) ((EntityPropertyImpl<?>) property).apply(player, entity, false, datas);
|
||||||
sendMetadata(player, entity, new ArrayList<>(datas.values()));
|
sendMetadata(player, entity, new ArrayList<>(datas.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMetadata(Player player, PacketEntity entity, List<EntityData> data) {
|
public void sendMetadata(Player player, PacketEntity entity, List<EntityData<?>> data) {
|
||||||
sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data));
|
sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +180,7 @@ public class V1_8PacketFactory implements PacketFactory {
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void add(Map<Integer, EntityData> map, EntityData data) {
|
protected void add(Map<Integer, EntityData<?>> map, EntityData<?> data) {
|
||||||
map.put(data.getIndex(), data);
|
map.put(data.getIndex(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,16 @@ package lol.pyr.znpcsplus.skin;
|
||||||
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
|
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
|
||||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.NameFetchingDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
||||||
|
import lol.pyr.znpcsplus.skin.descriptor.UUIDFetchingDescriptor;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public interface BaseSkinDescriptor extends SkinDescriptor {
|
public interface BaseSkinDescriptor extends SkinDescriptor {
|
||||||
|
@ -22,7 +24,14 @@ public interface BaseSkinDescriptor extends SkinDescriptor {
|
||||||
static BaseSkinDescriptor deserialize(MojangSkinCache skinCache, String str) {
|
static BaseSkinDescriptor deserialize(MojangSkinCache skinCache, String str) {
|
||||||
String[] arr = str.split(";");
|
String[] arr = str.split(";");
|
||||||
if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor(skinCache);
|
if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor(skinCache);
|
||||||
else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(skinCache, String.join(";", Arrays.copyOfRange(arr, 1, arr.length)));
|
else if (arr[0].equalsIgnoreCase("fetching-uuid")) {
|
||||||
|
String value = String.join(";", Arrays.copyOfRange(arr, 1, arr.length));
|
||||||
|
return new UUIDFetchingDescriptor(skinCache, UUID.fromString(value));
|
||||||
|
}
|
||||||
|
else if(arr[0].equalsIgnoreCase("fetching")) {
|
||||||
|
String value = String.join(";", Arrays.copyOfRange(arr, 1, arr.length));
|
||||||
|
return new NameFetchingDescriptor(skinCache, value);
|
||||||
|
}
|
||||||
else if (arr[0].equalsIgnoreCase("prefetched")) {
|
else if (arr[0].equalsIgnoreCase("prefetched")) {
|
||||||
List<TextureProperty> properties = new ArrayList<>();
|
List<TextureProperty> properties = new ArrayList<>();
|
||||||
for (int i = 0; i < (arr.length - 1) / 3; i++) {
|
for (int i = 0; i < (arr.length - 1) / 3; i++) {
|
||||||
|
|
|
@ -3,12 +3,14 @@ package lol.pyr.znpcsplus.skin;
|
||||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
|
import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
|
||||||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.NameFetchingDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
|
||||||
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
|
||||||
|
import lol.pyr.znpcsplus.skin.descriptor.UUIDFetchingDescriptor;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
|
public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
|
||||||
private final MojangSkinCache skinCache;
|
private final MojangSkinCache skinCache;
|
||||||
|
@ -26,7 +28,12 @@ public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SkinDescriptor createRefreshingDescriptor(String playerName) {
|
public SkinDescriptor createRefreshingDescriptor(String playerName) {
|
||||||
return new FetchingDescriptor(skinCache, playerName);
|
return new NameFetchingDescriptor(skinCache, playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkinDescriptor createRefreshingDescriptor(UUID playerUUID) {
|
||||||
|
return new UUIDFetchingDescriptor(skinCache, playerUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,4 +59,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,8 +47,8 @@ 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.minecraftservices.com/minecraft/profile/lookup/name/" + name);
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
connection = (HttpURLConnection) url.openConnection();
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@ import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class FetchingDescriptor implements BaseSkinDescriptor, SkinDescriptor {
|
public class NameFetchingDescriptor implements BaseSkinDescriptor, SkinDescriptor {
|
||||||
private final MojangSkinCache skinCache;
|
private final MojangSkinCache skinCache;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
public FetchingDescriptor(MojangSkinCache skinCache, String name) {
|
public NameFetchingDescriptor(MojangSkinCache skinCache, String name) {
|
||||||
this.skinCache = skinCache;
|
this.skinCache = skinCache;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package lol.pyr.znpcsplus.skin.descriptor;
|
||||||
|
|
||||||
|
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||||
|
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
|
||||||
|
import lol.pyr.znpcsplus.skin.SkinImpl;
|
||||||
|
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class UUIDFetchingDescriptor implements BaseSkinDescriptor, SkinDescriptor {
|
||||||
|
|
||||||
|
private final MojangSkinCache skinCache;
|
||||||
|
private final UUID uuid;
|
||||||
|
|
||||||
|
public UUIDFetchingDescriptor(MojangSkinCache skinCache, UUID uuid) {
|
||||||
|
this.skinCache = skinCache;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<SkinImpl> fetch(Player player) {
|
||||||
|
return skinCache.fetchByUUID(uuid.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkinImpl fetchInstant(Player player) {
|
||||||
|
return fetch(player).join();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInstant(Player player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serialize() {
|
||||||
|
return "fetching-uuid;" + uuid.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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,55 +4,95 @@ 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 {
|
||||||
private final static List<WeakReference<Viewable>> all = new ArrayList<>();
|
private final static List<WeakReference<Viewable>> all = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
public static List<Viewable> all() {
|
public static List<Viewable> all() {
|
||||||
|
synchronized (all) {
|
||||||
all.removeIf(reference -> reference.get() == null);
|
all.removeIf(reference -> reference.get() == null);
|
||||||
return all.stream()
|
return all.stream()
|
||||||
.map(Reference::get)
|
.map(Reference::get)
|
||||||
.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();
|
||||||
|
synchronized (all) {
|
||||||
|
all.removeIf(reference -> reference.get() == null || reference.get() == this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +103,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 +117,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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue