Compare commits
60
Commits
old/java17
..
3.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec3db2908d | ||
|
|
f753f46572 | ||
|
|
e3c1ec2cd5 | ||
|
|
3954b36b0c | ||
|
|
378f6d44b5 | ||
|
|
9ddad39117 | ||
|
|
9ba2cc3ed0 | ||
|
|
f9a5b7ff77 | ||
|
|
6f9c7bab89 | ||
|
|
16fbcbf6ab | ||
|
|
66a109a7ae | ||
|
|
17e5f2763b | ||
|
|
3b8ba0dfc6 | ||
|
|
6dd78201a9 | ||
|
|
6907575ec3 | ||
|
|
747359af68 | ||
|
|
40d84cbc5e | ||
|
|
75db8d7541 | ||
|
|
eff10fde6b | ||
|
|
883f859420 | ||
|
|
e6981cfb57 | ||
|
|
9e2f65fcff | ||
|
|
56035b4520 | ||
|
|
a682b053d0 | ||
|
|
e2f478a0d9 | ||
|
|
e6729db398 | ||
|
|
f64f8de6c3 | ||
|
|
2f0c72aea7 | ||
|
|
f7d31f14e1 | ||
|
|
24bfc15d38 | ||
|
|
7cefd10a21 | ||
|
|
7989b6c19a | ||
|
|
2d7d6de987 | ||
|
|
9beec39415 | ||
|
|
1c1a899e5c | ||
|
|
cd7ae4c5e1 | ||
|
|
025d4d2dc5 | ||
|
|
1620dc38d0 | ||
|
|
a4f653258f | ||
|
|
baf0c726c8 | ||
|
|
6057793496 | ||
|
|
55cf62e1d8 | ||
|
|
ad7450d6fa | ||
|
|
72c48a6a10 | ||
|
|
4249b97e8f | ||
|
|
68872959f8 | ||
|
|
fb193a7f15 | ||
|
|
a3185768a3 | ||
|
|
3318583475 | ||
|
|
1fb35efd76 | ||
|
|
e12d0a21ee | ||
|
|
3362b2a16f | ||
|
|
eeb7b8fe05 | ||
|
|
bce700c7e3 | ||
|
|
9506ed628b | ||
|
|
c07a411fe2 | ||
|
|
b8ea7f6ad7 | ||
|
|
83de88b171 | ||
|
|
6dffed2e8f | ||
|
|
6e7fbf240f |
No files matched your search
@@ -44,7 +44,7 @@ This method requires [NpcPlugin](https://www.spigotmc.org/resources/npc-plugin-1
|
||||
<dependency>
|
||||
<groupId>com.github.Eisi05</groupId>
|
||||
<artifactId>NpcApi</artifactId>
|
||||
<version>2.3.3</version>
|
||||
<version>3.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
@@ -60,7 +60,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.github.Eisi05:NpcApi:2.3.3'
|
||||
compileOnly 'com.github.Eisi05:NpcApi:3.4.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -93,7 +93,7 @@ Add the repository and dependency to your `pom.xml`:
|
||||
<dependency>
|
||||
<groupId>com.github.Eisi05</groupId>
|
||||
<artifactId>NpcApi</artifactId>
|
||||
<version>2.3.3</version>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
@@ -110,7 +110,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.Eisi05:NpcApi:2.3.3'
|
||||
implementation 'com.github.Eisi05:NpcApi:3.4.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -229,6 +229,6 @@ NPC npc = NpcManager.fromUUID(npcUuid);
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java 17+
|
||||
- Spigot 1.17+ (compatible with newer versions)
|
||||
- Java 21+
|
||||
- Spigot 1.20.6+ (compatible with newer versions)
|
||||
- Minecraft server with NPC support
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
jdk:
|
||||
- openjdk17
|
||||
- openjdk21
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
|
||||
<groupId>de.eisi05</groupId>
|
||||
<artifactId>NpcApi</artifactId>
|
||||
<version>2.3.3</version>
|
||||
<version>3.4.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>NpcApi</name>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -64,7 +66,7 @@
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.17.1-R0.1-SNAPSHOT</version>
|
||||
<version>1.20.6-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -103,5 +105,11 @@
|
||||
<version>1.10.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
@@ -3,6 +3,7 @@ package de.eisi05.npc.api;
|
||||
import de.eisi05.npc.api.listeners.*;
|
||||
import de.eisi05.npc.api.manager.NpcManager;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.objects.NameDisplayOptions;
|
||||
import de.eisi05.npc.api.objects.NpcConfig;
|
||||
import de.eisi05.npc.api.objects.NpcHolder;
|
||||
import de.eisi05.npc.api.pathfinding.Path;
|
||||
@@ -12,6 +13,7 @@ import de.eisi05.npc.api.utils.PacketReader;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedPlayerTeam;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
@@ -40,6 +42,8 @@ public final class NpcApi
|
||||
public static Plugin plugin;
|
||||
public static Function<Player, String> DISABLED_MESSAGE_PROVIDER = player -> ChatColor.RED + "DISABLED";
|
||||
|
||||
public static NamespacedKey displayKey;
|
||||
|
||||
/**
|
||||
* The configuration object for the NPC API, containing various settings like the look-at timer.
|
||||
*/
|
||||
@@ -58,6 +62,7 @@ public final class NpcApi
|
||||
{
|
||||
NpcApi.plugin = plugin;
|
||||
NpcApi.config = config;
|
||||
displayKey = new NamespacedKey(NpcApi.plugin, "display");
|
||||
|
||||
if(config.preciseSleepingHitbox())
|
||||
listeners.add(sleepListener = new NpcSleepListener());
|
||||
@@ -71,6 +76,8 @@ public final class NpcApi
|
||||
Tasks.start();
|
||||
PacketReader.injectAll();
|
||||
|
||||
NameDisplayOptions.NameDisplayDefaults.reload();
|
||||
|
||||
new Metrics(plugin, 28179).addCustomChart(new Metrics.SingleLineChart("npcCount", () -> NpcManager.getList().size()));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
package de.eisi05.npc.api.ai;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.utils.RegistryPredicate;
|
||||
import de.eisi05.npc.api.utils.SerializableBiPredicate;
|
||||
import de.eisi05.npc.api.utils.SerializablePredicate;
|
||||
import de.eisi05.npc.api.utils.serialize.NpcRegistry;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Base abstract class for NPC AI goals. Goals represent specific behaviors that an NPC can perform, such as walking to a location, attacking an entity, or
|
||||
@@ -17,8 +28,15 @@ public abstract class Goal implements Serializable
|
||||
@Serial
|
||||
private final static long serialVersionUID = 1L;
|
||||
private Priority priority;
|
||||
|
||||
@Deprecated(since = "2.4.1")
|
||||
@SerializedName("legacyCondition")
|
||||
private SerializablePredicate<NPC> condition;
|
||||
|
||||
@SerializedName("condition")
|
||||
@JsonAdapter(GoalConditionAdapter.class)
|
||||
private SerializableBiPredicate<Location, NPC> newCondition;
|
||||
|
||||
/**
|
||||
* Creates a new goal with the specified priority.
|
||||
*
|
||||
@@ -33,14 +51,22 @@ public abstract class Goal implements Serializable
|
||||
{
|
||||
}
|
||||
|
||||
@Serial
|
||||
private Object readResolve()
|
||||
{
|
||||
if(newCondition == null && condition != null)
|
||||
newCondition = new RegistryPredicate<>(NpcRegistry.EXPRESSION_PARSER, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the condition for this goal. The condition is checked before the goal is executed.
|
||||
*
|
||||
* @return The condition for this goal
|
||||
* @return The registry predicate condition for this goal, or {@code null} if not set or incompatible
|
||||
*/
|
||||
public @Nullable SerializablePredicate<NPC> getCondition()
|
||||
public final @Nullable RegistryPredicate<Location> getCondition()
|
||||
{
|
||||
return condition;
|
||||
return newCondition instanceof RegistryPredicate<Location> predicate ? predicate : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,9 +74,9 @@ public abstract class Goal implements Serializable
|
||||
*
|
||||
* @param condition The condition to set
|
||||
*/
|
||||
public void setCondition(@Nullable SerializablePredicate<NPC> condition)
|
||||
public void setCondition(@Nullable RegistryPredicate<Location> condition)
|
||||
{
|
||||
this.condition = condition;
|
||||
this.newCondition = condition;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +107,7 @@ public abstract class Goal implements Serializable
|
||||
*/
|
||||
protected boolean canUse(@NotNull NPC npc)
|
||||
{
|
||||
return condition == null || condition.test(npc);
|
||||
return newCondition == null || newCondition.test(getLocation(), npc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +131,16 @@ public abstract class Goal implements Serializable
|
||||
*/
|
||||
protected abstract void stop(@NotNull NPC npc);
|
||||
|
||||
/**
|
||||
* Gets the location associated with this goal.
|
||||
*
|
||||
* @return The location or null if not applicable
|
||||
*/
|
||||
protected @Nullable Location getLocation()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this goal can continue executing. If this returns false, the goal will be stopped and a new goal will be selected.
|
||||
*
|
||||
@@ -113,7 +149,7 @@ public abstract class Goal implements Serializable
|
||||
*/
|
||||
protected boolean canContinue(@NotNull NPC npc)
|
||||
{
|
||||
return condition == null || condition.test(npc);
|
||||
return newCondition == null || newCondition.test(getLocation(), npc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,4 +225,81 @@ public abstract class Goal implements Serializable
|
||||
this.weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gson JSON adapter for serializing and deserializing location-based NPC predicates for AI goals.
|
||||
*/
|
||||
private static class GoalConditionAdapter
|
||||
implements JsonSerializer<SerializableBiPredicate<Location, NPC>>, JsonDeserializer<SerializableBiPredicate<Location, NPC>>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(SerializableBiPredicate<Location, NPC> src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if(src == null)
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
if(src instanceof RegistryPredicate predicate)
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty(predicate.key(), predicate.expression() != null ? predicate.expression() : "");
|
||||
return obj;
|
||||
}
|
||||
|
||||
String expression = src.toString();
|
||||
if(expression != null && !expression.contains("$$Lambda") && !expression.contains("@"))
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("expression", expression);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return JsonNull.INSTANCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public SerializableBiPredicate<Location, NPC> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if(json == null || json.isJsonNull())
|
||||
return null;
|
||||
|
||||
if(json.isJsonPrimitive())
|
||||
return new RegistryPredicate<>(NpcRegistry.EXPRESSION_PARSER, json.getAsString());
|
||||
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
|
||||
if(obj.has("key"))
|
||||
{
|
||||
String key = obj.get("key").getAsString();
|
||||
String expression = obj.has("expression") && !obj.get("expression").isJsonNull() ? obj.get("expression").getAsString() : null;
|
||||
return new RegistryPredicate<>(key, expression);
|
||||
}
|
||||
|
||||
if(obj.entrySet().size() == 1)
|
||||
{
|
||||
var entry = obj.entrySet().iterator().next();
|
||||
String key = entry.getKey();
|
||||
JsonElement val = entry.getValue();
|
||||
|
||||
String expression = (val != null && !val.isJsonNull() && !val.getAsString().isEmpty()) ? val.getAsString() : null;
|
||||
return new RegistryPredicate<>(key, expression);
|
||||
}
|
||||
|
||||
if(obj.has("lambda"))
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] data = Base64.getDecoder().decode(obj.get("lambda").getAsString());
|
||||
try(ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais))
|
||||
{
|
||||
return (SerializableBiPredicate<Location, NPC>) ois.readObject();
|
||||
}
|
||||
}
|
||||
catch(Exception ignored) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,7 +274,7 @@ public class GoalSelector
|
||||
if(usableGoals.isEmpty())
|
||||
selectedGoal = null;
|
||||
else if(usableGoals.size() == 1)
|
||||
selectedGoal = usableGoals.get(0);
|
||||
selectedGoal = usableGoals.getFirst();
|
||||
else
|
||||
{
|
||||
int totalWeight = usableGoals.stream()
|
||||
@@ -300,7 +300,7 @@ public class GoalSelector
|
||||
}
|
||||
|
||||
if(selectedGoal == null)
|
||||
selectedGoal = usableGoals.get(usableGoals.size() - 1);
|
||||
selectedGoal = usableGoals.getLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package de.eisi05.npc.api.ai.goals;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import de.eisi05.npc.api.ai.Goal;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.objects.NpcOption;
|
||||
import de.eisi05.npc.api.utils.LocationUtils;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.SerializableBiPredicate;
|
||||
import de.eisi05.npc.api.utils.*;
|
||||
import de.eisi05.npc.api.utils.serialize.NpcRegistry;
|
||||
import de.eisi05.npc.api.wrapper.enums.Pose;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntityData;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedServerPlayer;
|
||||
@@ -25,20 +26,41 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A goal that makes the NPC attack nearby entities that match a predicate. The attack behavior varies based on the item in the NPC's main hand: -
|
||||
* Bow/Trident/Crossbow: Long range attacks - Sword/Axe: Short range attacks - Other: Short range attacks Only activates when a valid target is in range and
|
||||
* line of sight. Once a target is locked, the NPC continues attacking until the target becomes unreachable or invalid.
|
||||
* A goal that makes the NPC attack nearby entities that match a predicate. The attack behavior varies based on the item in the NPC's main hand:
|
||||
* <ul>
|
||||
* <li><b>WeaponMechanics guns:</b> Custom ranged attack behavior</li>
|
||||
* <li><b>Bow/Trident/Crossbow:</b> Long range attacks</li>
|
||||
* <li><b>Sword/Axe:</b> Short range melee attacks</li>
|
||||
* <li><b>Other:</b> Default melee attacks</li>
|
||||
* </ul>
|
||||
* Only activates when a valid target is in range and line of sight. Once a target is locked, the NPC continues attacking until the target becomes
|
||||
* unreachable or invalid.
|
||||
*/
|
||||
public class AttackEntityGoal extends Goal
|
||||
{
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Attribute ATTACK__DAMAGE_ATTRIBUTE =
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V1_21_2) ?
|
||||
Attribute.GENERIC_ATTACK_DAMAGE : Reflections.getStaticField(Attribute.class, "ATTACK_DAMAGE");
|
||||
private static final Attribute KNOCKBACK_RESISTANCE_ATTRIBUTE =
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V1_21_2) ?
|
||||
Attribute.GENERIC_KNOCKBACK_RESISTANCE : Reflections.getStaticField(Attribute.class, "KNOCKBACK_RESISTANCE");
|
||||
private static final Attribute ATTACK_KNOCKBACK_ATTRIBUTE =
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V1_21_2) ?
|
||||
Attribute.GENERIC_ATTACK_KNOCKBACK : Reflections.getStaticField(Attribute.class, "ATTACK_KNOCKBACK");
|
||||
|
||||
|
||||
private static final double WEAPONMECHANICS_ATTACK_RANGE = 25.0;
|
||||
private static final double BOW_ATTACK_RANGE = 15.0;
|
||||
private static final double MELEE_ATTACK_RANGE = 3.0;
|
||||
@@ -48,6 +70,7 @@ public class AttackEntityGoal extends Goal
|
||||
private static final double KITING_DISTANCE = 3.0;
|
||||
private static final double OPTIMAL_RANGED_DISTANCE = 6.0;
|
||||
|
||||
@JsonAdapter(TargetFilterAdapter.class)
|
||||
private SerializableBiPredicate<LivingEntity, NPC> targetFilter;
|
||||
private double customAttackRange;
|
||||
private double speed;
|
||||
@@ -67,7 +90,7 @@ public class AttackEntityGoal extends Goal
|
||||
*
|
||||
* @param targetFilter A predicate to filter valid targets
|
||||
*/
|
||||
public AttackEntityGoal(@NotNull SerializableBiPredicate<LivingEntity, NPC> targetFilter)
|
||||
public AttackEntityGoal(@NotNull RegistryPredicate<LivingEntity> targetFilter)
|
||||
{
|
||||
this(targetFilter, -1);
|
||||
}
|
||||
@@ -78,27 +101,28 @@ public class AttackEntityGoal extends Goal
|
||||
* @param targetFilter A predicate to filter valid targets
|
||||
* @param customAttackRange Custom attack range (-1 for default based on weapon)
|
||||
*/
|
||||
public AttackEntityGoal(@NotNull SerializableBiPredicate<LivingEntity, NPC> targetFilter, double customAttackRange)
|
||||
public AttackEntityGoal(@NotNull RegistryPredicate<LivingEntity> targetFilter, double customAttackRange)
|
||||
{
|
||||
this(targetFilter, customAttackRange, WalkToLocationGoal.DEFAULT_SPEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AttackEntityGoal with a filter for valid targets and custom speed.
|
||||
* Creates an AttackEntityGoal with a filter for valid targets, custom attack range, and custom movement speed.
|
||||
*
|
||||
* @param targetFilter A predicate to filter valid targets
|
||||
* @param speed Custom movement speed (-1 for default)
|
||||
* @param targetFilter A predicate to filter valid targets
|
||||
* @param customAttackRange Custom attack range (-1 for default based on weapon)
|
||||
* @param speed Custom movement speed (clamped between 0.1 and 1.0)
|
||||
*/
|
||||
public AttackEntityGoal(@NotNull SerializableBiPredicate<LivingEntity, NPC> targetFilter, double customAttackRange, double speed)
|
||||
public AttackEntityGoal(@NotNull RegistryPredicate<LivingEntity> targetFilter, double customAttackRange, double speed)
|
||||
{
|
||||
super(Priority.ALWAYS);
|
||||
this.targetFilter = targetFilter;
|
||||
this.customAttackRange = customAttackRange;
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this goal.
|
||||
* Copy constructor for duplicating an existing AttackEntityGoal.
|
||||
*
|
||||
* @param goal the goal to copy
|
||||
*/
|
||||
@@ -113,19 +137,19 @@ public class AttackEntityGoal extends Goal
|
||||
/**
|
||||
* Gets the target filter for this goal.
|
||||
*
|
||||
* @return the target filter
|
||||
* @return the target filter as a {@link RegistryPredicate}, or {@code null} if incompatible
|
||||
*/
|
||||
public SerializableBiPredicate<LivingEntity, NPC> getTargetFilter()
|
||||
public @Nullable RegistryPredicate<LivingEntity> getTargetFilter()
|
||||
{
|
||||
return targetFilter;
|
||||
return targetFilter instanceof RegistryPredicate<LivingEntity> registryPredicate ? registryPredicate : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target filter for this goal.
|
||||
*
|
||||
* @param targetFilter the new target filter
|
||||
* @param targetFilter the new target filter predicate
|
||||
*/
|
||||
public void setTargetFilter(@NotNull SerializableBiPredicate<LivingEntity, NPC> targetFilter)
|
||||
public void setTargetFilter(@Nullable RegistryPredicate<LivingEntity> targetFilter)
|
||||
{
|
||||
this.targetFilter = targetFilter;
|
||||
}
|
||||
@@ -385,7 +409,7 @@ public class AttackEntityGoal extends Goal
|
||||
{
|
||||
Location npcLoc = npc.getLocation();
|
||||
Location below = npcLoc.clone().subtract(0, 0.1, 0);
|
||||
|
||||
|
||||
if(below.getBlock().getType().isAir())
|
||||
return false;
|
||||
}
|
||||
@@ -513,7 +537,7 @@ public class AttackEntityGoal extends Goal
|
||||
if(meta == null)
|
||||
return 5;
|
||||
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(Attribute.GENERIC_ATTACK_SPEED);
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(ATTACK__DAMAGE_ATTRIBUTE);
|
||||
if(modifiers == null)
|
||||
return 5;
|
||||
|
||||
@@ -553,7 +577,7 @@ public class AttackEntityGoal extends Goal
|
||||
{
|
||||
return (String) Reflections.invokeMethod("me.deecaad.weaponmechanics.WeaponMechanicsAPI", "getWeaponTitle", item).get();
|
||||
}
|
||||
catch (NoClassDefFoundError | Exception e)
|
||||
catch(NoClassDefFoundError | Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -747,7 +771,7 @@ public class AttackEntityGoal extends Goal
|
||||
if(meta == null)
|
||||
return 0.5;
|
||||
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(Attribute.GENERIC_ATTACK_DAMAGE);
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(ATTACK__DAMAGE_ATTRIBUTE);
|
||||
if(modifiers == null)
|
||||
return 0.5;
|
||||
|
||||
@@ -786,7 +810,7 @@ public class AttackEntityGoal extends Goal
|
||||
if(meta == null)
|
||||
return 0.5;
|
||||
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(Attribute.GENERIC_ATTACK_KNOCKBACK);
|
||||
Collection<AttributeModifier> modifiers = meta.getAttributeModifiers(ATTACK_KNOCKBACK_ATTRIBUTE);
|
||||
if(modifiers == null)
|
||||
return 0.5;
|
||||
|
||||
@@ -805,10 +829,10 @@ public class AttackEntityGoal extends Goal
|
||||
*/
|
||||
private double getKnockbackResistance(@NotNull LivingEntity target)
|
||||
{
|
||||
if(target.getAttribute(Attribute.GENERIC_KNOCKBACK_RESISTANCE) == null)
|
||||
if(target.getAttribute(KNOCKBACK_RESISTANCE_ATTRIBUTE) == null)
|
||||
return 0;
|
||||
|
||||
return target.getAttribute(Attribute.GENERIC_KNOCKBACK_RESISTANCE).getValue();
|
||||
return target.getAttribute(KNOCKBACK_RESISTANCE_ATTRIBUTE).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -898,4 +922,83 @@ public class AttackEntityGoal extends Goal
|
||||
movementGoal = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gson JSON adapter for serializing and deserializing entity targeting predicates.
|
||||
*/
|
||||
private static class TargetFilterAdapter
|
||||
implements JsonSerializer<SerializableBiPredicate<LivingEntity, NPC>>, JsonDeserializer<SerializableBiPredicate<LivingEntity, NPC>>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(SerializableBiPredicate<LivingEntity, NPC> src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if(src == null)
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
if(src instanceof RegistryPredicate predicate)
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty(predicate.key(), predicate.expression() != null ? predicate.expression() : "");
|
||||
return obj;
|
||||
}
|
||||
|
||||
String expression = src.toString();
|
||||
if(expression != null && !expression.contains("$$Lambda") && !expression.contains("@"))
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("expression", expression);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return JsonNull.INSTANCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public SerializableBiPredicate<LivingEntity, NPC> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException
|
||||
{
|
||||
if(json == null || json.isJsonNull())
|
||||
return null;
|
||||
|
||||
if(json.isJsonPrimitive())
|
||||
return new RegistryPredicate<>(NpcRegistry.EXPRESSION_PARSER, json.getAsString());
|
||||
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
|
||||
if(obj.has("key"))
|
||||
{
|
||||
String key = obj.get("key").getAsString();
|
||||
String expression = obj.has("expression") ? obj.get("expression").getAsString() : null;
|
||||
return new RegistryPredicate<>(key, expression);
|
||||
}
|
||||
|
||||
if(obj.entrySet().size() == 1)
|
||||
{
|
||||
var entry = obj.entrySet().iterator().next();
|
||||
String key = entry.getKey();
|
||||
JsonElement val = entry.getValue();
|
||||
|
||||
String expression = (val != null && !val.isJsonNull() && !val.getAsString().isEmpty()) ? val.getAsString() : null;
|
||||
return new RegistryPredicate<>(key, expression);
|
||||
}
|
||||
|
||||
if(obj.has("lambda") || obj.has("lamda"))
|
||||
{
|
||||
String base64Str = obj.has("lambda") ? obj.get("lambda").getAsString() : obj.get("lamda").getAsString();
|
||||
try
|
||||
{
|
||||
byte[] data = Base64.getDecoder().decode(base64Str);
|
||||
try(ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais))
|
||||
{
|
||||
return (SerializableBiPredicate<LivingEntity, NPC>) ois.readObject();
|
||||
}
|
||||
}
|
||||
catch(Exception ignored) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class FollowEntityGoal extends Goal
|
||||
this.targetEntityId = targetEntityId;
|
||||
this.followDistance = Math.max(stopDistance + 0.5, followDistance);
|
||||
this.stopDistance = Math.max(0.5, stopDistance);
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,7 +175,7 @@ public class LookAroundGoal extends Goal
|
||||
newPitch = lerp(currentPitch, targetPitch, rotationSpeed);
|
||||
}
|
||||
|
||||
newPitch = Math.max(-PITCH_MIN_MAX, Math.min(PITCH_MIN_MAX, newPitch));
|
||||
newPitch = Math.clamp(newPitch, -PITCH_MIN_MAX, PITCH_MIN_MAX);
|
||||
|
||||
currentLoc.setYaw(newYaw);
|
||||
currentLoc.setPitch(newPitch);
|
||||
@@ -278,7 +278,7 @@ public class LookAroundGoal extends Goal
|
||||
targetYaw = (float) Math.toDegrees(Math.atan2(-dx, dz));
|
||||
targetPitch = (float) Math.toDegrees(-Math.atan2(dy, distanceXZ));
|
||||
|
||||
targetPitch = Math.max(-PITCH_MIN_MAX, Math.min(PITCH_MIN_MAX, targetPitch));
|
||||
targetPitch = Math.clamp(targetPitch, -PITCH_MIN_MAX, PITCH_MIN_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@ public class WalkPathGoal extends Goal
|
||||
super(Priority.MEDIUM);
|
||||
this.path = builder.path;
|
||||
this.serializablePath = path != null ? path.toSerializablePath() : null;
|
||||
this.speed = Math.max(0.1, Math.min(1.0, builder.speed));
|
||||
this.speed = Math.clamp(builder.speed, 0.1, 1.0);
|
||||
this.completionCallback = builder.completionCallback;
|
||||
this.withRotation = builder.withRotation;
|
||||
}
|
||||
@@ -73,6 +73,9 @@ public class WalkPathGoal extends Goal
|
||||
*/
|
||||
public @Nullable Path getPath()
|
||||
{
|
||||
if(path == null && serializablePath != null)
|
||||
path = serializablePath.toPath();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -104,7 +107,7 @@ public class WalkPathGoal extends Goal
|
||||
*/
|
||||
public void setSpeed(double speed)
|
||||
{
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +151,15 @@ public class WalkPathGoal extends Goal
|
||||
this.withRotation = withRotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Location getLocation()
|
||||
{
|
||||
if(path == null && serializablePath != null)
|
||||
path = serializablePath.toPath();
|
||||
|
||||
return path != null ? path.asLocations().getLast() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this goal can be used by the NPC.
|
||||
*
|
||||
@@ -163,6 +175,9 @@ public class WalkPathGoal extends Goal
|
||||
if(!super.canUse(npc))
|
||||
return false;
|
||||
|
||||
if(path == null && serializablePath != null)
|
||||
path = serializablePath.toPath();
|
||||
|
||||
if(path == null)
|
||||
return false;
|
||||
|
||||
@@ -170,7 +185,7 @@ public class WalkPathGoal extends Goal
|
||||
if(waypoints == null || waypoints.isEmpty())
|
||||
return false;
|
||||
|
||||
Location startLocation = waypoints.get(0);
|
||||
Location startLocation = waypoints.getFirst();
|
||||
if(!startLocation.getWorld().equals(npc.getLocation().getWorld()))
|
||||
return false;
|
||||
|
||||
@@ -186,6 +201,9 @@ public class WalkPathGoal extends Goal
|
||||
@Override
|
||||
public void start(@NotNull NPC npc)
|
||||
{
|
||||
if(path == null && serializablePath != null)
|
||||
path = serializablePath.toPath();
|
||||
|
||||
if(path == null)
|
||||
return;
|
||||
|
||||
@@ -193,7 +211,7 @@ public class WalkPathGoal extends Goal
|
||||
if(waypoints == null || waypoints.isEmpty())
|
||||
return;
|
||||
|
||||
Location startLocation = waypoints.get(0);
|
||||
Location startLocation = waypoints.getFirst();
|
||||
double distance = npc.getLocation().distance(startLocation);
|
||||
|
||||
isWalking = true;
|
||||
@@ -232,6 +250,9 @@ public class WalkPathGoal extends Goal
|
||||
*/
|
||||
private void walkPath(@NotNull NPC npc)
|
||||
{
|
||||
if(path == null && serializablePath != null)
|
||||
path = serializablePath.toPath();
|
||||
|
||||
if(path == null)
|
||||
return;
|
||||
|
||||
@@ -244,7 +265,7 @@ public class WalkPathGoal extends Goal
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
|
||||
Location finalLocation = waypoints.get(waypoints.size() - 1);
|
||||
Location finalLocation = waypoints.getLast();
|
||||
|
||||
npc.walkTo(path, speed, true, result ->
|
||||
{
|
||||
@@ -265,9 +286,7 @@ public class WalkPathGoal extends Goal
|
||||
public void tick(@NotNull NPC npc)
|
||||
{
|
||||
if(walkingToStart && walkToStartGoal != null)
|
||||
{
|
||||
walkToStartGoal.tick(npc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,7 +384,7 @@ public class WalkPathGoal extends Goal
|
||||
*/
|
||||
public Builder speed(double speed)
|
||||
{
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.objects.NpcOption;
|
||||
import de.eisi05.npc.api.pathfinding.AStarPathfinder;
|
||||
import de.eisi05.npc.api.pathfinding.Path;
|
||||
import de.eisi05.npc.api.pathfinding.PathfindingUtils;
|
||||
import de.eisi05.npc.api.scheduler.Tasks;
|
||||
import de.eisi05.npc.api.utils.SerializableConsumer;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
@@ -63,7 +62,7 @@ public class WalkToLocationGoal extends Goal
|
||||
super(Priority.MEDIUM);
|
||||
this.targetLocation = builder.targetLocation.clone();
|
||||
this.serializableLocation = new Path.SerializablePath.SerializableLocation(targetLocation);
|
||||
this.speed = Math.max(0.1, Math.min(1.0, builder.speed));
|
||||
this.speed = Math.clamp(builder.speed, 0.1, 1.0);
|
||||
this.maxIterations = builder.maxIterations;
|
||||
this.allowDiagonal = builder.allowDiagonal;
|
||||
this.completionCallback = builder.completionCallback;
|
||||
@@ -103,7 +102,7 @@ public class WalkToLocationGoal extends Goal
|
||||
*/
|
||||
public void setSpeed(double speed)
|
||||
{
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +200,9 @@ public class WalkToLocationGoal extends Goal
|
||||
if(!super.canUse(npc))
|
||||
return false;
|
||||
|
||||
if(targetLocation == null && serializableLocation != null)
|
||||
targetLocation = serializableLocation.toLocation();
|
||||
|
||||
if(targetLocation == null || !targetLocation.getWorld().equals(npc.getLocation().getWorld()))
|
||||
return false;
|
||||
|
||||
@@ -216,6 +218,12 @@ public class WalkToLocationGoal extends Goal
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Location getLocation()
|
||||
{
|
||||
return targetLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the walk to location goal, calculating the path.
|
||||
*
|
||||
@@ -225,7 +233,12 @@ public class WalkToLocationGoal extends Goal
|
||||
public void start(@NotNull NPC npc)
|
||||
{
|
||||
if(targetLocation == null)
|
||||
return;
|
||||
{
|
||||
if(serializableLocation == null)
|
||||
return;
|
||||
|
||||
targetLocation = serializableLocation.toLocation();
|
||||
}
|
||||
|
||||
calculatePath(npc);
|
||||
}
|
||||
@@ -280,6 +293,9 @@ public class WalkToLocationGoal extends Goal
|
||||
@Override
|
||||
public boolean canContinue(@NotNull NPC npc)
|
||||
{
|
||||
if(targetLocation == null && serializableLocation != null)
|
||||
targetLocation = serializableLocation.toLocation();
|
||||
|
||||
return isWalking && currentPath != null && targetLocation != null && npc.getLocation().distance(targetLocation) > 1.0
|
||||
&& super.canContinue(npc);
|
||||
}
|
||||
@@ -314,6 +330,9 @@ public class WalkToLocationGoal extends Goal
|
||||
*/
|
||||
public @NotNull Location getTargetLocation()
|
||||
{
|
||||
if(targetLocation == null && serializableLocation != null)
|
||||
targetLocation = serializableLocation.toLocation();
|
||||
|
||||
return targetLocation.clone();
|
||||
}
|
||||
|
||||
@@ -345,7 +364,7 @@ public class WalkToLocationGoal extends Goal
|
||||
return;
|
||||
}
|
||||
|
||||
CompletableFuture<Path> future = npc.findPathAsync(List.of(start, end), maxIterations, allowDiagonal, null);
|
||||
CompletableFuture<Path> future = npc.findPathAsync(null, List.of(start, end), maxIterations, allowDiagonal, null);
|
||||
Tasks.trackFuture(future);
|
||||
future.thenAcceptAsync(path -> pathable = path != null, task -> Bukkit.getScheduler().runTask(NpcApi.plugin, task))
|
||||
.exceptionally(e ->
|
||||
@@ -375,7 +394,7 @@ public class WalkToLocationGoal extends Goal
|
||||
|
||||
isWalking = true;
|
||||
|
||||
pathfindingFuture = npc.findPathAsync(List.of(start, end), maxIterations, allowDiagonal, null);
|
||||
pathfindingFuture = npc.findPathAsync(null, List.of(start, end), maxIterations, allowDiagonal, null);
|
||||
Tasks.trackFuture(pathfindingFuture);
|
||||
pathfindingFuture.thenAcceptAsync(path ->
|
||||
{
|
||||
@@ -540,7 +559,7 @@ public class WalkToLocationGoal extends Goal
|
||||
*/
|
||||
public Builder speed(double speed)
|
||||
{
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class WanderGoal extends Goal
|
||||
this.radius = radius;
|
||||
this.minDelay = minDelay;
|
||||
this.maxDelay = maxDelay;
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +158,7 @@ public class WanderGoal extends Goal
|
||||
*/
|
||||
public void setSpeed(double speed)
|
||||
{
|
||||
this.speed = Math.max(0.1, Math.min(1.0, speed));
|
||||
this.speed = Math.clamp(speed, 0.1, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +286,8 @@ public class WanderGoal extends Goal
|
||||
|
||||
if(y.isPresent())
|
||||
{
|
||||
targetLocation = new Location(world, x, y.getAsInt(), z);
|
||||
targetLocation = new Location(world, x, y.getAsInt(), z, 0, 0);
|
||||
targetLocation.setYaw(calculateYaw(currentLoc, targetLocation));
|
||||
currentWalkGoal = new WalkToLocationGoal.Builder(targetLocation).speed(speed).build();
|
||||
currentWalkGoal.start(npc);
|
||||
return;
|
||||
@@ -297,4 +298,17 @@ public class WanderGoal extends Goal
|
||||
|
||||
delayTicks = maxDelay;
|
||||
}
|
||||
|
||||
public float calculateYaw(@NotNull Location current, @NotNull Location target)
|
||||
{
|
||||
double dx = target.getX() - current.getX();
|
||||
double dz = target.getZ() - current.getZ();
|
||||
|
||||
double yaw = Math.toDegrees(Math.atan2(-dx, dz));
|
||||
|
||||
if(yaw < 0)
|
||||
yaw += 360;
|
||||
|
||||
return (float) yaw;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package de.eisi05.npc.api.manager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.utils.ObjectSaver;
|
||||
import de.eisi05.npc.api.utils.serialize.ObjectSaver;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@@ -12,6 +12,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
|
||||
@@ -48,9 +50,9 @@ public class NpcManager
|
||||
*
|
||||
* @return the set of NPCs
|
||||
*/
|
||||
public static @NotNull Collection<NPC> getList()
|
||||
public static @NotNull Set<NPC> getList()
|
||||
{
|
||||
return npcById.values();
|
||||
return new HashSet<>(npcById.values());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +77,8 @@ public class NpcManager
|
||||
public static void clear()
|
||||
{
|
||||
npcById.clear();
|
||||
toLoadNPCs.clear();
|
||||
loadExceptions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,16 +126,40 @@ public class NpcManager
|
||||
|
||||
long failCounter = 0;
|
||||
long successCounter = 0;
|
||||
long migrations = 0;
|
||||
|
||||
Exception exception = null;
|
||||
for(File file1 : files)
|
||||
{
|
||||
if(!file1.getName().endsWith(".npc"))
|
||||
if(!file1.getName().endsWith(".npc") && !file1.getName().endsWith(".npc.json"))
|
||||
continue;
|
||||
|
||||
if(NpcApi.config.debug())
|
||||
NpcApi.plugin.getLogger().info("Loading NPC: " + file1.getName());
|
||||
try
|
||||
{
|
||||
NPC.SerializedNPC serializedNPC = new ObjectSaver(file1).read();
|
||||
ObjectSaver saver = new ObjectSaver(file1);
|
||||
|
||||
NPC.SerializedNPC serializedNPC;
|
||||
if(!saver.isJson())
|
||||
{
|
||||
File backupFolder = new File(file, "backup");
|
||||
if (!backupFolder.exists())
|
||||
backupFolder.mkdirs();
|
||||
|
||||
File backupFile = new File(backupFolder, file1.getName());
|
||||
Files.copy(file1.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
serializedNPC = saver.read();
|
||||
file1.delete();
|
||||
|
||||
File jsonFile = new File(file, file1.getName() + ".json");
|
||||
new ObjectSaver(jsonFile).write(serializedNPC, false);
|
||||
migrations++;
|
||||
}
|
||||
else
|
||||
serializedNPC = saver.read(NPC.SerializedNPC.class);
|
||||
|
||||
Either<NPC, UUID> npcEither = serializedNPC.deserializedNPC();
|
||||
|
||||
if(npcEither.right().isPresent())
|
||||
@@ -154,6 +182,9 @@ public class NpcManager
|
||||
}
|
||||
}
|
||||
|
||||
if(migrations > 0)
|
||||
NpcApi.plugin.getLogger().info("Successfully migrated " + migrations + " NPC's");
|
||||
|
||||
if(successCounter == 1)
|
||||
NpcApi.plugin.getLogger().info("Successfully loaded " + successCounter + " NPC");
|
||||
else if(successCounter > 1)
|
||||
@@ -235,7 +266,9 @@ public class NpcManager
|
||||
if(either.left().isEmpty())
|
||||
return false;
|
||||
|
||||
loadNpc(either.left().get());
|
||||
NPC npc = either.left().get();
|
||||
npc.markChange();
|
||||
loadNpc(npc);
|
||||
return true;
|
||||
}
|
||||
catch(Exception e)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package de.eisi05.npc.api.movement;
|
||||
|
||||
import de.eisi05.npc.api.utils.ObjectSaver;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.utils.serialize.ObjectSaver;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -10,6 +13,7 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Creates a new MovementRecording instance.
|
||||
@@ -27,6 +31,51 @@ public record MovementRecording(@NotNull ArrayList<MovementData> movements, long
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Generates a synthetic MovementRecording between two locations.
|
||||
*
|
||||
* @param start Starting location
|
||||
* @param end Destination location
|
||||
* @param steps Number of intermediate steps (e.g., 10)
|
||||
* @param intervalTicks Delay between points in ticks (e.g., 1 or 2 ticks)
|
||||
* @param npc NPC associated with the movement playback
|
||||
* @return A constructed MovementRecording instance
|
||||
*/
|
||||
public static @NotNull MovementRecording createSyntheticRecording(@NotNull Location start, @NotNull Location end, long steps, int intervalTicks,
|
||||
@NotNull NPC npc
|
||||
)
|
||||
{
|
||||
ArrayList<MovementData> movements = new ArrayList<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
long stepDurationMs = intervalTicks * 50L;
|
||||
|
||||
double deltaX = (end.getX() - start.getX()) / steps;
|
||||
double deltaY = (end.getY() - start.getY()) / steps;
|
||||
double deltaZ = (end.getZ() - start.getZ()) / steps;
|
||||
|
||||
Vector dir = end.toVector().subtract(start.toVector());
|
||||
Location lookLocation = start.clone().setDirection(dir);
|
||||
float yaw = lookLocation.getYaw();
|
||||
float pitch = lookLocation.getPitch();
|
||||
|
||||
for(long i = 0; i <= steps; i++)
|
||||
{
|
||||
double x = start.getX() + (deltaX * i);
|
||||
double y = start.getY() + (deltaY * i);
|
||||
double z = start.getZ() + (deltaZ * i);
|
||||
long timestamp = (i * stepDurationMs);
|
||||
|
||||
Location location = new Location(start.getWorld(), x, y, z, yaw, pitch);
|
||||
MovementData data = new MovementData(location, timestamp);
|
||||
movements.add(data);
|
||||
}
|
||||
|
||||
long endTime = (steps * stepDurationMs);
|
||||
long sessionId = ThreadLocalRandom.current().nextLong(100_000L, 999_999L);
|
||||
|
||||
return new MovementRecording(movements, sessionId, startTime, endTime, npc.getUUID(), intervalTicks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a MovementRecording from a file.
|
||||
*
|
||||
@@ -87,7 +136,7 @@ public record MovementRecording(@NotNull ArrayList<MovementData> movements, long
|
||||
*/
|
||||
public @Nullable MovementData getFirstMovement()
|
||||
{
|
||||
return movements.isEmpty() ? null : movements.get(0);
|
||||
return movements.isEmpty() ? null : movements.getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +146,7 @@ public record MovementRecording(@NotNull ArrayList<MovementData> movements, long
|
||||
*/
|
||||
public @Nullable MovementData getLastMovement()
|
||||
{
|
||||
return movements.isEmpty() ? null : movements.get(movements.size() - 1);
|
||||
return movements.isEmpty() ? null : movements.getLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -135,7 +135,7 @@ public class MovementReplayer
|
||||
private long lastTimestamp;
|
||||
|
||||
private ReplaySession(@NotNull NPC npc, @NotNull MovementRecording recording, double speedMultiplier, boolean changeRealLocation,
|
||||
@Nullable Consumer<ReplayResult> onComplete, long replayId, @NotNull Player... viewers)
|
||||
@Nullable Consumer<ReplayResult> onComplete, long replayId, @Nullable Player... viewers)
|
||||
{
|
||||
this.npc = npc;
|
||||
this.recording = recording;
|
||||
@@ -146,7 +146,7 @@ public class MovementReplayer
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.currentIndex = 0;
|
||||
this.lastTimestamp = 0;
|
||||
this.viewers = viewers;
|
||||
this.viewers = viewers != null && viewers.length > 0 ? viewers : null;
|
||||
}
|
||||
|
||||
private void start()
|
||||
@@ -169,9 +169,23 @@ public class MovementReplayer
|
||||
|
||||
Location startLocation = firstMovement.toLocation(world);
|
||||
|
||||
npc.setEnabled(true);
|
||||
for(Player p : viewers)
|
||||
npc.showNPCToPlayer(p);
|
||||
if(viewers != null)
|
||||
{
|
||||
for(Player p : viewers)
|
||||
npc.showNPCToPlayer(p);
|
||||
}
|
||||
else if(npc.getVisibilityManager().shouldShowToAllPlayers())
|
||||
npc.showNpcToAllPlayers();
|
||||
else
|
||||
{
|
||||
for(UUID uuid : npc.getVisibilityManager().getSpecificPlayers())
|
||||
{
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if(player == null)
|
||||
continue;
|
||||
npc.showNPCToPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
Location currentLocation = npc.getLocation();
|
||||
npc.changeRealLocation(startLocation);
|
||||
@@ -188,6 +202,9 @@ public class MovementReplayer
|
||||
{
|
||||
if(currentIndex >= recording.getMovementCount())
|
||||
{
|
||||
if(!recording.movements().isEmpty())
|
||||
executeMovement(recording.getLastMovement());
|
||||
|
||||
complete(ReplayResult.COMPLETED);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import de.eisi05.npc.api.ai.GoalSelector;
|
||||
@@ -11,9 +12,10 @@ import de.eisi05.npc.api.events.NpcStartWalkingEvent;
|
||||
import de.eisi05.npc.api.interfaces.NpcClickAction;
|
||||
import de.eisi05.npc.api.manager.NpcManager;
|
||||
import de.eisi05.npc.api.manager.NpcVisibilityManager;
|
||||
import de.eisi05.npc.api.pathfinding.AbstractPathfinder;
|
||||
import de.eisi05.npc.api.pathfinding.PathfindingUtils;
|
||||
import de.eisi05.npc.api.scheduler.PathTask;
|
||||
import de.eisi05.npc.api.utils.ObjectSaver;
|
||||
import de.eisi05.npc.api.utils.serialize.ObjectSaver;
|
||||
import de.eisi05.npc.api.utils.Var;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.enums.Pose;
|
||||
@@ -37,7 +39,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
@@ -55,8 +56,8 @@ import java.util.stream.Collectors;
|
||||
public class NPC extends NpcHolder
|
||||
{
|
||||
public transient final Map<UUID, String> nameCache = new HashMap<>();
|
||||
final Map<String, Integer> toDeleteEntities = new HashMap<>();
|
||||
private final List<UUID> viewers = new ArrayList<>();
|
||||
transient final Map<UUID, Map<String, Integer>> toDeleteEntities = new HashMap<>();
|
||||
private final Set<UUID> viewers = new HashSet<>();
|
||||
private final Path npcPath;
|
||||
private final Map<UUID, PathTask> pathTasks = new HashMap<>();
|
||||
private final Set<PathTask> walkingTasks = new LinkedHashSet<>();
|
||||
@@ -113,7 +114,7 @@ public class NPC extends NpcHolder
|
||||
this.location = location;
|
||||
this.entity = this.serverPlayer = WrappedServerPlayer.create(location, uuid, name.isStatic() ? name.getName() : WrappedComponent.create(null), null);
|
||||
|
||||
npcPath = NpcApi.plugin.getDataFolder().toPath().resolve("NPC").resolve(uuid + ".npc");
|
||||
npcPath = NpcApi.plugin.getDataFolder().toPath().resolve("NPC").resolve(uuid + ".npc.json");
|
||||
|
||||
for(NpcOption<?, ?> value : NpcOption.values())
|
||||
setOption(value, Var.unsafeCast(value.getDefaultValue()));
|
||||
@@ -456,6 +457,60 @@ public class NPC extends NpcHolder
|
||||
return getOption(NpcOption.CUSTOM_DATA, GLOBAL_UUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tags associated with this NPC. The tags are stored as a set of strings.
|
||||
*
|
||||
* @return A {@code HashSet} containing the tags, or {@code null} if no tags are set
|
||||
* @see NpcOption#TAGS
|
||||
*/
|
||||
public @NotNull Set<String> getTags()
|
||||
{
|
||||
return getOption(NpcOption.TAGS, GLOBAL_UUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags associated with this NPC. The tags are stored as a set of strings.
|
||||
*
|
||||
* @param tags the set of tags to set
|
||||
* @see NpcOption#TAGS
|
||||
*/
|
||||
public void setTags(@NotNull Set<String> tags)
|
||||
{
|
||||
setOption(NpcOption.TAGS, tags, GLOBAL_UUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tag to this NPC. The tag is stored as a string and is added to the set of tags associated with the NPC.
|
||||
*
|
||||
* @param tag the tag to add
|
||||
* @return true if the tag was removed, false if the tag was not present
|
||||
* @see NpcOption#TAGS
|
||||
*/
|
||||
public boolean addTag(@NotNull String tag)
|
||||
{
|
||||
Set<String> tags = getTags();
|
||||
if(!tags.add(tag))
|
||||
return false;
|
||||
setOption(NpcOption.TAGS, tags, GLOBAL_UUID);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tag from this NPC. The tag is stored as a string and is removed from the set of tags associated with the NPC.
|
||||
*
|
||||
* @param tag the tag to remove
|
||||
* @return true if the tag was removed, false if the tag was not present
|
||||
* @see NpcOption#TAGS
|
||||
*/
|
||||
public boolean removeTag(@NotNull String tag)
|
||||
{
|
||||
Set<String> tags = getTags();
|
||||
if(!tags.remove(tag))
|
||||
return false;
|
||||
setOption(NpcOption.TAGS, tags, GLOBAL_UUID);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visibility manager for this NPC.
|
||||
*
|
||||
@@ -476,10 +531,9 @@ public class NPC extends NpcHolder
|
||||
public void updateName(@NotNull Player player)
|
||||
{
|
||||
WrappedServerPlayer.fromPlayer(player)
|
||||
.sendPacket(SetEntityDataPacket.create(serverPlayer.getNameTag().getId(), serverPlayer.getNameTag().applyData(
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V1_19_4) || isEnabled() ? name.getName(player) :
|
||||
WrappedComponent.parseFromLegacy(NpcApi.DISABLED_MESSAGE_PROVIDER.apply(player))
|
||||
.append(WrappedComponent.create("\n").append(name.getName(player))))));
|
||||
.sendPacket(SetEntityDataPacket.create(serverPlayer.getNameTag().getId(), serverPlayer.getNameTag()
|
||||
.applyData(isEnabled() ? name.getName(player) : WrappedComponent.parseFromLegacy(NpcApi.DISABLED_MESSAGE_PROVIDER.apply(player))
|
||||
.append(WrappedComponent.create("\n").append(name.getName(player))), name.getDisplayOptions())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -583,8 +637,8 @@ public class NPC extends NpcHolder
|
||||
if(npcPreShowEvent.isCancelled())
|
||||
return;
|
||||
|
||||
if(!viewers.contains(player.getUniqueId()))
|
||||
viewers.add(player.getUniqueId());
|
||||
if(!viewers.add(player.getUniqueId()))
|
||||
return;
|
||||
|
||||
WrappedServerPlayer wrappedServerPlayer = WrappedServerPlayer.fromPlayer(player);
|
||||
|
||||
@@ -595,29 +649,25 @@ public class NPC extends NpcHolder
|
||||
List<PacketWrapper> packets = new ArrayList<>();
|
||||
|
||||
Arrays.stream(NpcOption.values()).filter(NpcOption::loadBefore)
|
||||
.forEach(npcOption -> npcOption.getPacket(getOption(npcOption, player), this, player).ifPresent(packets::add));
|
||||
.forEach(npcOption -> npcOption.getPacket(this, player).ifPresent(packets::add));
|
||||
|
||||
if(!name.isStatic() && getOption(NpcOption.SHOW_TAB_LIST, player))
|
||||
setOption(NpcOption.SHOW_TAB_LIST, false);
|
||||
|
||||
//packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.ADD_PLAYER, serverPlayer));
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, serverPlayer));
|
||||
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V1_19_3))
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LISTED, serverPlayer));
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LISTED, serverPlayer));
|
||||
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V1_21_2))
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER, serverPlayer));
|
||||
|
||||
Arrays.stream(NpcOption.values()).filter(npcOption -> !npcOption.equals(NpcOption.ENABLED) && !npcOption.loadBefore())
|
||||
.forEach(npcOption -> npcOption.getPacket(getOption(npcOption, player), this, player).ifPresent(packets::add));
|
||||
.forEach(npcOption -> npcOption.getPacket(this, player).ifPresent(packets::add));
|
||||
|
||||
NpcOption.ENABLED.getPacket(isEnabled(), this, player).ifPresent(packets::add);
|
||||
NpcOption.ENABLED.getPacket(this, player).ifPresent(packets::add);
|
||||
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, wrappedServerPlayer));
|
||||
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V1_19_3))
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LISTED, wrappedServerPlayer));
|
||||
packets.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LISTED, wrappedServerPlayer));
|
||||
|
||||
packets.forEach(wrappedServerPlayer::sendPacket);
|
||||
|
||||
@@ -638,6 +688,7 @@ public class NPC extends NpcHolder
|
||||
}
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(this::hideNpcFromPlayer);
|
||||
toDeleteEntities.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,6 +710,7 @@ public class NPC extends NpcHolder
|
||||
|
||||
WrappedServerPlayer wrappedServerPlayer = WrappedServerPlayer.fromPlayer(player);
|
||||
wrappedServerPlayer.sendPacket(new RemoveEntityPacket(serverPlayer.getId()));
|
||||
wrappedServerPlayer.sendPacket(new RemoveEntityPacket(entity.getId()));
|
||||
wrappedServerPlayer.sendPacket(new RemoveEntityPacket(serverPlayer.getNameTag().getId()));
|
||||
|
||||
if(WrappedPlayerTeam.exists(player, getServerPlayer().getName()))
|
||||
@@ -668,12 +720,11 @@ public class NPC extends NpcHolder
|
||||
WrappedPlayerTeam.clear(player.getUniqueId(), getServerPlayer().getName());
|
||||
}
|
||||
|
||||
toDeleteEntities.values().forEach(integer -> wrappedServerPlayer.sendPacket(new RemoveEntityPacket(integer)));
|
||||
var values = toDeleteEntities.remove(player.getUniqueId());
|
||||
if(values != null)
|
||||
values.values().forEach(integer -> wrappedServerPlayer.sendPacket(new RemoveEntityPacket(integer)));
|
||||
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_19_3))
|
||||
wrappedServerPlayer.sendPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.REMOVE_PLAYER, serverPlayer));
|
||||
else
|
||||
wrappedServerPlayer.sendPacket(new PlayerInfoRemovePacket(List.of(getUUID())));
|
||||
wrappedServerPlayer.sendPacket(new PlayerInfoRemovePacket(List.of(getUUID())));
|
||||
|
||||
viewers.remove(player.getUniqueId());
|
||||
|
||||
@@ -832,22 +883,24 @@ public class NPC extends NpcHolder
|
||||
* This method executes the pathfinding logic off the main thread using the Bukkit scheduler.
|
||||
* </p>
|
||||
*
|
||||
* @param pathfinderFactory the factory to create the pathfinder, if null the default factory {@link NpcConfig#pathfinderFactory()} will be used
|
||||
* @param waypoints the list of locations the path must pass through, must not be null
|
||||
* @param maxIterations the maximum number of iterations allowed for the pathfinding algorithm before giving up
|
||||
* @param allowDiagonalMovement {@code true} to allow diagonal movement between nodes, {@code false} for straight lines only
|
||||
* @param progressListener an optional listener to receive progress updates (current iteration, max iterations), can be null
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return a {@link CompletableFuture} that will complete with the calculated {@link de.eisi05.npc.api.pathfinding.Path}
|
||||
* @throws RuntimeException if an underlying {@link PathfindingUtils.PathfindingException} occurs during execution
|
||||
*/
|
||||
public @NotNull CompletableFuture<de.eisi05.npc.api.pathfinding.Path> findPathAsync(@NotNull List<Location> waypoints, int maxIterations,
|
||||
public @NotNull CompletableFuture<de.eisi05.npc.api.pathfinding.Path> findPathAsync(@Nullable AbstractPathfinder.PathfinderFactory<?> pathfinderFactory,
|
||||
@NotNull List<Location> waypoints, int maxIterations,
|
||||
boolean allowDiagonalMovement,
|
||||
@Nullable BiConsumer<Integer, Integer> progressListener)
|
||||
@Nullable BiConsumer<Double, Integer> progressListener)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return findPath(waypoints, maxIterations, allowDiagonalMovement, progressListener);
|
||||
return findPath(pathfinderFactory, waypoints, maxIterations, allowDiagonalMovement, progressListener);
|
||||
}
|
||||
catch(PathfindingUtils.PathfindingException e)
|
||||
{
|
||||
@@ -859,20 +912,22 @@ public class NPC extends NpcHolder
|
||||
/**
|
||||
* Synchronously calculates a path through the specified waypoints based on the entity's current bounding box and scale.
|
||||
*
|
||||
* @param pathfinderFactory the factory to create the pathfinder, if null the default factory {@link NpcConfig#pathfinderFactory()} will be used
|
||||
* @param waypoints the list of locations the path must pass through, must not be null
|
||||
* @param maxIterations the maximum number of iterations allowed for the pathfinding algorithm before giving up
|
||||
* @param allowDiagonalMovement {@code true} to allow diagonal movement between nodes, {@code false} for straight lines only
|
||||
* @param progressListener an optional listener to receive progress updates (current iteration, max iterations), can be null
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return the calculated {@link de.eisi05.npc.api.pathfinding.Path}, must not be null
|
||||
* @throws PathfindingUtils.PathfindingException if the pathfinding algorithm fails to find a valid path or encounters an error
|
||||
*/
|
||||
public @NotNull de.eisi05.npc.api.pathfinding.Path findPath(@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
@Nullable BiConsumer<Integer, Integer> progressListener)
|
||||
public @NotNull de.eisi05.npc.api.pathfinding.Path findPath(@Nullable AbstractPathfinder.PathfinderFactory<?> pathfinderFactory,
|
||||
@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
@Nullable BiConsumer<Double, Integer> progressListener)
|
||||
throws PathfindingUtils.PathfindingException
|
||||
{
|
||||
WrappedEntity.BoundingBox boundingBox = entity.getBoundingBox();
|
||||
double scale = getOption(NpcOption.SCALE);
|
||||
return PathfindingUtils.findPath(waypoints, maxIterations, allowDiagonalMovement, boundingBox.getYSize() * scale,
|
||||
return PathfindingUtils.findPath(pathfinderFactory, waypoints, maxIterations, allowDiagonalMovement, boundingBox.getYSize() * scale,
|
||||
boundingBox.getXSize() * scale, progressListener);
|
||||
}
|
||||
|
||||
@@ -953,7 +1008,7 @@ public class NPC extends NpcHolder
|
||||
if(isWalking(player))
|
||||
cancelWalking(player);
|
||||
}
|
||||
final double speed = Math.max(Math.min(walkSpeed, 1), 0.1);
|
||||
final double speed = Math.clamp(walkSpeed, 0.1, 1);
|
||||
NpcStartWalkingEvent event = new NpcStartWalkingEvent(this, path, speed, changeRealLocation);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if(event.isCancelled())
|
||||
@@ -1427,9 +1482,6 @@ public class NPC extends NpcHolder
|
||||
* <li>Creation timestamp</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This class supports both legacy (pre-2.0.0) and current serialization formats
|
||||
* through the {@link #readResolve()} method for backward compatibility.
|
||||
*
|
||||
* @see SerializedNPC#serializedNPC(NPC)
|
||||
* @see #deserializedNPC()
|
||||
*/
|
||||
@@ -1438,54 +1490,15 @@ public class NPC extends NpcHolder
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final @NotNull UUID id;
|
||||
private final @NotNull Serializable name;
|
||||
private final @NotNull NpcName name;
|
||||
private final @Nullable NpcClickAction clickEvent;
|
||||
private final @NotNull Instant createdAt;
|
||||
private @NotNull UUID world;
|
||||
private double x, y, z;
|
||||
private float yaw, pitch;
|
||||
/**
|
||||
* Legacy options storage (pre-2.0.0).
|
||||
*
|
||||
* @deprecated Replaced by {@link #newOptions} in version 2.0.0
|
||||
*/
|
||||
@Deprecated(since = "2.0.0")
|
||||
private @NotNull Map<String, ? extends Serializable> options;
|
||||
private @NotNull Map<String, HashMap<String, Serializable>> newOptions;
|
||||
|
||||
/**
|
||||
* Creates a new serialized NPC instance (legacy constructor).
|
||||
*
|
||||
* @param world The UUID of the world the NPC is in
|
||||
* @param x The x-coordinate of the NPC
|
||||
* @param y The y-coordinate of the NPC
|
||||
* @param z The z-coordinate of the NPC
|
||||
* @param yaw The yaw rotation of the NPC
|
||||
* @param pitch The pitch rotation of the NPC
|
||||
* @param id The unique identifier of the NPC
|
||||
* @param name The serialized name of the NPC
|
||||
* @param options The NPC's options in legacy format
|
||||
* @param clickEvent The click action handler, if any
|
||||
* @param createdAt When the NPC was created
|
||||
* @deprecated Replaced by
|
||||
* {@link SerializedNPC#SerializedNPC(UUID, double, double, double, float, float, UUID, Serializable, NpcClickAction, Instant, Map)} in version 2.0.0
|
||||
*/
|
||||
@Deprecated(since = "2.0.0")
|
||||
private SerializedNPC(@NotNull UUID world, double x, double y, double z, float yaw, float pitch, @NotNull UUID id, @NotNull Serializable name,
|
||||
@NotNull Map<String, ? extends Serializable> options, @Nullable NpcClickAction clickEvent, @NotNull Instant createdAt)
|
||||
{
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.options = options;
|
||||
this.clickEvent = clickEvent;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
@SerializedName("options")
|
||||
private final @NotNull Map<String, HashMap<String, Serializable>> newOptions;
|
||||
|
||||
/**
|
||||
* Creates a new serialized NPC instance (current format).
|
||||
@@ -1502,7 +1515,7 @@ public class NPC extends NpcHolder
|
||||
* @param createdAt When the NPC was created
|
||||
* @param options The NPC's options in current format
|
||||
*/
|
||||
private SerializedNPC(@NotNull UUID world, double x, double y, double z, float yaw, float pitch, @NotNull UUID id, @NotNull Serializable name,
|
||||
private SerializedNPC(@NotNull UUID world, double x, double y, double z, float yaw, float pitch, @NotNull UUID id, @NotNull NpcName name,
|
||||
@Nullable NpcClickAction clickEvent, @NotNull Instant createdAt, @NotNull Map<String, HashMap<String, Serializable>> options)
|
||||
{
|
||||
this.world = world;
|
||||
@@ -1539,24 +1552,6 @@ public class NPC extends NpcHolder
|
||||
npc.getNpcName(), npc.clickEvent, npc.createdAt, map);
|
||||
}
|
||||
|
||||
@Serial
|
||||
private Object readResolve() throws ObjectStreamException
|
||||
{
|
||||
NpcName fixedName;
|
||||
if(name instanceof NpcName sn)
|
||||
fixedName = sn;
|
||||
else if(name instanceof WrappedComponent.SerializedComponent oldName)
|
||||
fixedName = NpcName.of(oldName.deserialize());
|
||||
else
|
||||
throw new IllegalStateException("Unexpected type for name field: " + name.getClass());
|
||||
|
||||
Map<String, HashMap<String, Serializable>> fixedOptions = newOptions == null ? new HashMap<>() : newOptions;
|
||||
if(fixedOptions.isEmpty() && options != null)
|
||||
options.forEach((s, serializable) -> fixedOptions.computeIfAbsent(GLOBAL_UUID.toString(), s1 -> new HashMap<>()).put(s, serializable));
|
||||
|
||||
return new SerializedNPC(world, x, y, z, yaw, pitch, id, fixedName, clickEvent, createdAt, fixedOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes this {@link SerializedNPC} object back into a fully functional {@link NPC} instance.
|
||||
*
|
||||
@@ -1571,24 +1566,15 @@ public class NPC extends NpcHolder
|
||||
if(world1 == null)
|
||||
return Either.right(world);
|
||||
|
||||
NPC npc = new NPC(new Location(world1, x, y, z, yaw, pitch), id, (NpcName) name).setClickEvent(
|
||||
clickEvent == null ? clickEvent : clickEvent.initialize());
|
||||
NPC npc = new NPC(new Location(world1, x, y, z, yaw, pitch), id, name).setClickEvent(clickEvent == null ? clickEvent : clickEvent.initialize());
|
||||
|
||||
if(options != null)
|
||||
{
|
||||
options.forEach((s, serializable) ->
|
||||
{
|
||||
Map<String, Serializable> map = (Map<String, Serializable>) serializable;
|
||||
map.forEach((s1, serializable1) -> NpcOption.getOption(s1)
|
||||
.ifPresent(npcOption -> npc.setOption((NpcOption<T, S>) npcOption, (T) npcOption.deserialize(Var.unsafeCast(serializable1)),
|
||||
UUID.fromString(s))));
|
||||
});
|
||||
}
|
||||
if(newOptions != null)
|
||||
newOptions.forEach((s, stringSerializableHashMap) -> stringSerializableHashMap.forEach((s1, serializable) ->
|
||||
NpcOption.getOption(s1)
|
||||
.ifPresent(npcOption -> npc.setOption((NpcOption<T, S>) npcOption, (T) npcOption.deserialize(Var.unsafeCast(serializable)),
|
||||
UUID.fromString(s)))));
|
||||
newOptions.forEach((s, stringSerializableHashMap) ->
|
||||
stringSerializableHashMap.forEach((s1, serializable) ->
|
||||
NpcOption.getOption(s1)
|
||||
.ifPresent(npcOption ->
|
||||
npc.setOption((NpcOption<T, S>) npcOption, (T) npcOption.deserialize(Var.unsafeCast(serializable)),
|
||||
UUID.fromString(s)))));
|
||||
|
||||
npc.createdAt = createdAt == null ? Instant.now() : createdAt;
|
||||
return Either.left(npc);
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.TextDisplay;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the layout and visual rendering options for an NPC's name display. Maps directly to Minecraft's Text Display entity metadata properties.
|
||||
*/
|
||||
public class NameDisplayOptions implements Serializable
|
||||
{
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private float height = 0;
|
||||
private float[] scale = NameDisplayDefaults.DEFAULT_SCALE.clone();
|
||||
private int brightness = NameDisplayDefaults.DEFAULT_BRIGHTNESS;
|
||||
private float viewRange = NameDisplayDefaults.DEFAULT_VIEW_RANGE;
|
||||
private int lineWidth = NameDisplayDefaults.DEFAULT_LINE_WIDTH;
|
||||
private int backgroundColor = NameDisplayDefaults.DEFAULT_BACKGROUND_COLOR;
|
||||
private byte textOpacity = NameDisplayDefaults.DEFAULT_TEXT_OPACITY;
|
||||
private boolean isSeeThrough = NameDisplayDefaults.DEFAULT_SEE_THROUGH;
|
||||
private int alignment = NameDisplayDefaults.DEFAULT_ALIGNMENT;
|
||||
|
||||
/**
|
||||
* Creates a new configuration instance initialized with standard Minecraft text display default settings.
|
||||
*/
|
||||
public NameDisplayOptions()
|
||||
{
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Getters & Fluent Setters
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Gets the 3D scale vector of the name display text.
|
||||
*
|
||||
* @return a float array of size 3 representing [x, y, z] scale dimensions
|
||||
*/
|
||||
public float[] getScale()
|
||||
{
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 3D scale dimensions of the name text using a pre-configured array. Passing {@code null} resets the scale to the default
|
||||
* {@code [1.0f, 1.0f, 1.0f]}.
|
||||
*
|
||||
* @param scale a float array representing [x, y, z], or null to reset
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setScale(float[] scale)
|
||||
{
|
||||
this.scale = scale != null ? scale.clone() : new float[]{1.0f, 1.0f, 1.0f};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 3D scale dimensions of the name text using individual coordinate factors.
|
||||
*
|
||||
* @param x the scale factor along the X-axis
|
||||
* @param y the scale factor along the Y-axis
|
||||
* @param z the scale factor along the Z-axis
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setScale(float x, float y, float z)
|
||||
{
|
||||
this.scale = new float[]{x, y, z};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packed block and skylight override value for the text display.
|
||||
*
|
||||
* @return the packed brightness value, or -1 if using the default environmental lighting
|
||||
*/
|
||||
public int getBrightness()
|
||||
{
|
||||
return this.brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the packed block and skylight override value for the text display. Setting this to -1 reverts the rendering back to natural entity lighting.
|
||||
*
|
||||
* @param brightness the packed brightness integer values
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setBrightness(int brightness)
|
||||
{
|
||||
this.brightness = brightness;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum block distance at which this name display is visible to players.
|
||||
*
|
||||
* @return the maximum rendering range in blocks
|
||||
*/
|
||||
public float getViewRange()
|
||||
{
|
||||
return this.viewRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum block distance at which this name display remains visible.
|
||||
*
|
||||
* @param viewRange the maximum rendering range in blocks
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setViewRange(float viewRange)
|
||||
{
|
||||
this.viewRange = viewRange;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line split threshold length before the text automatically wraps to a new line.
|
||||
*
|
||||
* @return the line width threshold value
|
||||
*/
|
||||
public int getLineWidth()
|
||||
{
|
||||
return this.lineWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line split threshold length before the text automatically wraps to a new line.
|
||||
*
|
||||
* @param lineWidth the line wrap boundary width (Minecraft default is typically 200)
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setLineWidth(int lineWidth)
|
||||
{
|
||||
this.lineWidth = lineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ARGB background color rendered directly behind the name text.
|
||||
*
|
||||
* @return the background color packed integer
|
||||
*/
|
||||
public int getBackgroundColor()
|
||||
{
|
||||
return this.backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ARGB background color rendered directly behind the name text. Use an alpha channel of {@code 0x00} to render an entirely transparent
|
||||
* background.
|
||||
*
|
||||
* @param backgroundColor the packed ARGB background integer code
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setBackgroundColor(int backgroundColor)
|
||||
{
|
||||
this.backgroundColor = backgroundColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specific opacity level applied to the rendered text characters.
|
||||
*
|
||||
* @return the opacity value index, where -1 signals default full alpha opacity
|
||||
*/
|
||||
public byte getTextOpacity()
|
||||
{
|
||||
return this.textOpacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specific opacity level applied to the rendered text characters.
|
||||
*
|
||||
* @param textOpacity alpha channel depth values, or -1 for default full opacity
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setTextOpacity(byte textOpacity)
|
||||
{
|
||||
this.textOpacity = textOpacity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the text characters remain visible through solid geometry blocks.
|
||||
*
|
||||
* @return true if the name text is see-through, false otherwise
|
||||
*/
|
||||
public boolean isSeeThrough()
|
||||
{
|
||||
return this.isSeeThrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the text characters remain visible through solid geometry blocks. Enabling this acts like an X-ray effect for the text.
|
||||
*
|
||||
* @param seeThrough true to force see-through rendering, false for occluded behavior
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setSeeThrough(boolean seeThrough)
|
||||
{
|
||||
this.isSeeThrough = seeThrough;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public @NotNull TextDisplay.TextAlignment getAlignment()
|
||||
{
|
||||
return TextDisplay.TextAlignment.values()[this.alignment];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal alignment formatting applied to multi-line text strings.
|
||||
*
|
||||
* @param alignment alignment
|
||||
* @return this options instance for method chaining
|
||||
*/
|
||||
public @NotNull NameDisplayOptions setAlignment(@NotNull TextDisplay.TextAlignment alignment)
|
||||
{
|
||||
this.alignment = alignment.ordinal();
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getHeight()
|
||||
{
|
||||
return this.height;
|
||||
}
|
||||
|
||||
@NotNull NameDisplayOptions setHeight(float height)
|
||||
{
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Utility Methods
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Creates a deep copy of this NameDisplayOptions instance. Essential for cloning NPCs without transferring internal field references.
|
||||
*
|
||||
* @return a completely independent, deep-copied duplicate of this configuration
|
||||
*/
|
||||
public @NotNull NameDisplayOptions copy()
|
||||
{
|
||||
NameDisplayOptions cloned = new NameDisplayOptions();
|
||||
cloned.scale = this.scale.clone();
|
||||
cloned.brightness = this.brightness;
|
||||
cloned.viewRange = this.viewRange;
|
||||
cloned.lineWidth = this.lineWidth;
|
||||
cloned.backgroundColor = this.backgroundColor;
|
||||
cloned.textOpacity = this.textOpacity;
|
||||
cloned.isSeeThrough = this.isSeeThrough;
|
||||
cloned.alignment = this.alignment;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this configuration containing the values of all internal display metrics.
|
||||
*
|
||||
* @return a descriptive text block summary of this options instance
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "NameDisplayOptions{" +
|
||||
"scale=" + Arrays.toString(scale) +
|
||||
", brightness=" + brightness +
|
||||
", viewRange=" + viewRange +
|
||||
", lineWidth=" + lineWidth +
|
||||
", backgroundColor=" + backgroundColor +
|
||||
", textOpacity=" + textOpacity +
|
||||
", isSeeThrough=" + isSeeThrough +
|
||||
", alignment=" + alignment +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class NameDisplayDefaults extends NameDisplayOptions
|
||||
{
|
||||
private static final File file;
|
||||
private static YamlConfiguration config;
|
||||
private static NameDisplayDefaults INSTANCE;
|
||||
|
||||
private static float[] DEFAULT_SCALE;
|
||||
private static int DEFAULT_BRIGHTNESS;
|
||||
private static float DEFAULT_VIEW_RANGE;
|
||||
private static int DEFAULT_LINE_WIDTH;
|
||||
private static int DEFAULT_BACKGROUND_COLOR;
|
||||
private static byte DEFAULT_TEXT_OPACITY;
|
||||
private static boolean DEFAULT_SEE_THROUGH;
|
||||
private static int DEFAULT_ALIGNMENT;
|
||||
|
||||
static
|
||||
{
|
||||
file = new File(NpcApi.plugin.getDataFolder(), "config.yml");
|
||||
reload();
|
||||
}
|
||||
|
||||
private NameDisplayDefaults() {}
|
||||
|
||||
public static NameDisplayOptions getInstance()
|
||||
{
|
||||
if(INSTANCE == null)
|
||||
INSTANCE = new NameDisplayDefaults();
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static void save()
|
||||
{
|
||||
try
|
||||
{
|
||||
config.save(file);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload()
|
||||
{
|
||||
config = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Double> list = (List<Double>) config.get("name-display.scale", List.of(1.0D, 1.0D, 1.0D));
|
||||
DEFAULT_SCALE = new float[]{list.get(0).floatValue(), list.get(1).floatValue(), list.get(2).floatValue()};
|
||||
DEFAULT_BRIGHTNESS = config.getInt("name-display.brightness", -1);
|
||||
DEFAULT_VIEW_RANGE = (float) config.getDouble("name-display.view-range", 1.0f);
|
||||
DEFAULT_LINE_WIDTH = config.getInt("name-display.line-width", 200);
|
||||
DEFAULT_BACKGROUND_COLOR = config.getInt("name-display.background-color", 0x40000000);
|
||||
DEFAULT_TEXT_OPACITY = (byte) config.getInt("name-display.text-opacity", 255);
|
||||
DEFAULT_SEE_THROUGH = config.getBoolean("name-display.see-through", false);
|
||||
DEFAULT_ALIGNMENT = config.getInt("name-display.text-alignment", TextDisplay.TextAlignment.CENTER.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getScale()
|
||||
{
|
||||
return DEFAULT_SCALE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setScale(float[] defaultScale)
|
||||
{
|
||||
DEFAULT_SCALE = defaultScale;
|
||||
config.set("name-display.scale", List.of(DEFAULT_SCALE[0], DEFAULT_SCALE[1], DEFAULT_SCALE[2]));
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightness()
|
||||
{
|
||||
return DEFAULT_BRIGHTNESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setBrightness(int defaultBrightness)
|
||||
{
|
||||
DEFAULT_BRIGHTNESS = defaultBrightness;
|
||||
config.set("name-display.brightness", DEFAULT_BRIGHTNESS);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getViewRange()
|
||||
{
|
||||
return DEFAULT_VIEW_RANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setViewRange(float defaultViewRange)
|
||||
{
|
||||
DEFAULT_VIEW_RANGE = defaultViewRange;
|
||||
config.set("name-display.view-range", DEFAULT_VIEW_RANGE);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineWidth()
|
||||
{
|
||||
return DEFAULT_LINE_WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setLineWidth(int defaultLineWidth)
|
||||
{
|
||||
DEFAULT_LINE_WIDTH = defaultLineWidth;
|
||||
config.set("name-display.line-width", DEFAULT_LINE_WIDTH);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBackgroundColor()
|
||||
{
|
||||
return DEFAULT_BACKGROUND_COLOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setBackgroundColor(int defaultBackgroundColor)
|
||||
{
|
||||
DEFAULT_BACKGROUND_COLOR = defaultBackgroundColor;
|
||||
config.set("name-display.background-color", DEFAULT_BACKGROUND_COLOR);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getTextOpacity()
|
||||
{
|
||||
return DEFAULT_TEXT_OPACITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setTextOpacity(byte defaultTextOpacity)
|
||||
{
|
||||
DEFAULT_TEXT_OPACITY = defaultTextOpacity;
|
||||
config.set("name-display.text-opacity", DEFAULT_TEXT_OPACITY);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeeThrough()
|
||||
{
|
||||
return DEFAULT_SEE_THROUGH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setSeeThrough(boolean defaultSeeThrough)
|
||||
{
|
||||
DEFAULT_SEE_THROUGH = defaultSeeThrough;
|
||||
config.set("name-display.see-through", DEFAULT_SEE_THROUGH);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TextDisplay.TextAlignment getAlignment()
|
||||
{
|
||||
return TextDisplay.TextAlignment.values()[DEFAULT_ALIGNMENT];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NameDisplayDefaults setAlignment(TextDisplay.TextAlignment defaultAlignment)
|
||||
{
|
||||
DEFAULT_ALIGNMENT = defaultAlignment.ordinal();
|
||||
config.set("name-display.text-alignment", DEFAULT_ALIGNMENT);
|
||||
save();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import de.eisi05.npc.api.pathfinding.AbstractPathfinder;
|
||||
import de.eisi05.npc.api.utils.ApiOnly;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Configuration settings for NPC behavior. This class allows for customizing various aspects of an NPC, such as interaction timers.
|
||||
*/
|
||||
@@ -91,11 +95,24 @@ public class NpcConfig
|
||||
private boolean preciseSleepingHitbox = false;
|
||||
|
||||
/**
|
||||
* If true, the NPC will asynchronously load unloaded chunks that it is walking into.
|
||||
* Default: false
|
||||
* If true, the NPC will asynchronously load unloaded chunks that it is walking into. Default: false
|
||||
*/
|
||||
private boolean loadChunksOnPath = false;
|
||||
|
||||
/**
|
||||
* The default factory used to create pathfinders for this NPC.
|
||||
* <p>
|
||||
* Default: {@link AbstractPathfinder.PathfinderFactory#ASTAR}
|
||||
*/
|
||||
private AbstractPathfinder.PathfinderFactory pathfinderFactory = AbstractPathfinder.PathfinderFactory.ASTAR;
|
||||
|
||||
/**
|
||||
* A predicate that determines whether a block is passable for pathfinding.
|
||||
* <p>
|
||||
* Default: {@code block -> false}
|
||||
*/
|
||||
private Predicate<Block> pathfindingPassableOverride = block -> false;
|
||||
|
||||
/**
|
||||
* Sets the duration an NPC will look at a player after an interaction.
|
||||
*
|
||||
@@ -251,6 +268,30 @@ public class NpcConfig
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pathfinder factory that dictates how paths are calculated.
|
||||
*
|
||||
* @param factory the pathfinder factory to use
|
||||
* @return This {@link NpcConfig} instance for method chaining. Never null.
|
||||
*/
|
||||
public @NotNull NpcConfig pathfinderFactory(@NotNull AbstractPathfinder.PathfinderFactory factory)
|
||||
{
|
||||
this.pathfinderFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate that determines whether a block is passable for pathfinding.
|
||||
*
|
||||
* @param pathfindingPassableOverride The predicate to use for pathfinding passability.
|
||||
* @return This {@link NpcConfig} instance for method chaining. Never null.
|
||||
*/
|
||||
public @NotNull NpcConfig pathfindingPassableOverride(@NotNull Predicate<Block> pathfindingPassableOverride)
|
||||
{
|
||||
this.pathfindingPassableOverride = pathfindingPassableOverride;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured duration an NPC will look at a player.
|
||||
*
|
||||
@@ -370,4 +411,24 @@ public class NpcConfig
|
||||
{
|
||||
return loadChunksOnPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pathfinder factory mapped to the configuration.
|
||||
*
|
||||
* @return the factory
|
||||
*/
|
||||
public @NotNull AbstractPathfinder.PathfinderFactory pathfinderFactory()
|
||||
{
|
||||
return pathfinderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predicate that determines whether a block is passable for pathfinding.
|
||||
*
|
||||
* @return the predicate
|
||||
*/
|
||||
public @NotNull Predicate<Block> pathfindingPassableOverride()
|
||||
{
|
||||
return pathfindingPassableOverride;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public abstract class NpcHolder implements InventoryHolder
|
||||
if(player == null)
|
||||
return;
|
||||
|
||||
option.getPacket(value, npc, player).ifPresent(packetWrapper -> WrappedServerPlayer.fromPlayer(player).sendPacket(packetWrapper));
|
||||
option.getPacket(npc, player).ifPresent(packetWrapper -> WrappedServerPlayer.fromPlayer(player).sendPacket(packetWrapper));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,62 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.SerializableFunction;
|
||||
import de.eisi05.npc.api.utils.Var;
|
||||
import de.eisi05.npc.api.utils.serialize.NpcRegistry;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Represents the name of an NPC, which can be either a fixed {@link WrappedComponent}
|
||||
* or dynamically generated based on a {@link Player}.
|
||||
* Represents the name of an NPC, which can be either a fixed {@link WrappedComponent} or dynamically generated based on a {@link Player}.
|
||||
*/
|
||||
@JsonAdapter(NpcName.NpcNameAdapter.class)
|
||||
public class NpcName implements Serializable
|
||||
{
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final WrappedComponent.SerializedComponent nameComponentSerialized;
|
||||
private final SerializableFunction<Player, WrappedComponent.SerializedComponent> nameFunctionSerialized;
|
||||
static
|
||||
{
|
||||
NpcRegistry.registerNameFunction(NpcRegistry.KEY_PLACEHOLDER_API, (player, placeholder) ->
|
||||
{
|
||||
String newName = placeholder.replace("&", "§")
|
||||
.replace("\r\n", "\\n")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\n");
|
||||
boolean hasColor = Arrays.stream(ChatColor.values())
|
||||
.anyMatch(chatColor -> newName.contains(chatColor.toString()) && chatColor.isColor());
|
||||
|
||||
private transient final WrappedComponent nameComponent;
|
||||
private transient final SerializableFunction<Player, WrappedComponent> nameFunction;
|
||||
if(!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
return hasColor ? WrappedComponent.parseFromLegacy(newName) : WrappedComponent.parseFromLegacy(ChatColor.WHITE + newName);
|
||||
|
||||
String placeHolder = ((String) Reflections.invokeStaticMethod("me.clip.placeholderapi.PlaceholderAPI", "setPlaceholders", player, newName)
|
||||
.get()).replace("&", "§");
|
||||
return hasColor ? WrappedComponent.parseFromLegacy(placeHolder) : WrappedComponent.parseFromLegacy(ChatColor.WHITE + placeHolder);
|
||||
});
|
||||
}
|
||||
|
||||
private WrappedComponent.SerializedComponent nameComponentSerialized;
|
||||
private String nameFunctionKey;
|
||||
private transient WrappedComponent nameComponent;
|
||||
private transient SerializableFunction<Player, WrappedComponent.SerializedComponent> nameFunctionSerialized;
|
||||
private transient SerializableFunction<Player, WrappedComponent> nameFunction;
|
||||
private NameDisplayOptions displayOptions = new NameDisplayOptions();
|
||||
|
||||
/**
|
||||
* Creates a static NPC name.
|
||||
@@ -33,35 +66,28 @@ public class NpcName implements Serializable
|
||||
private NpcName(@NotNull WrappedComponent nameComponent)
|
||||
{
|
||||
this.nameComponent = nameComponent;
|
||||
this.nameFunction = null;
|
||||
|
||||
this.nameComponentSerialized = nameComponent.serialize();
|
||||
this.nameFunctionSerialized = null;
|
||||
this.nameFunctionKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dynamic NPC name with a fallback static component.
|
||||
* <p>
|
||||
* The {@code nameFunction} generates the name for a player, but if needed,
|
||||
* {@code fallback} will be used as a default static name.
|
||||
* Creates a dynamic NPC name with a lookup key and a fallback static component.
|
||||
*
|
||||
* @param nameFunction the function producing the name for a given player
|
||||
* @param fallback the static fallback name component
|
||||
* @param nameFunctionKey the registry key used to resolve the dynamic function at runtime
|
||||
* @param fallback the static fallback name component used if the key is missing or generation fails
|
||||
*/
|
||||
private NpcName(@NotNull SerializableFunction<Player, WrappedComponent> nameFunction, @NotNull WrappedComponent fallback)
|
||||
private NpcName(@NotNull String nameFunctionKey, @NotNull WrappedComponent fallback)
|
||||
{
|
||||
this.nameComponent = fallback;
|
||||
this.nameFunction = nameFunction;
|
||||
|
||||
this.nameComponentSerialized = fallback.serialize();
|
||||
this.nameFunctionSerialized = player -> nameFunction.apply(player).serialize();
|
||||
this.nameFunctionKey = nameFunctionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NpcName} with a static name.
|
||||
*
|
||||
* @param name the fixed name component
|
||||
* @return a new NpcName instance
|
||||
* @return a new static NpcName instance
|
||||
*/
|
||||
public static @NotNull NpcName of(@NotNull WrappedComponent name)
|
||||
{
|
||||
@@ -71,11 +97,10 @@ public class NpcName implements Serializable
|
||||
/**
|
||||
* Creates a new {@link NpcName} from a legacy text string.
|
||||
* <p>
|
||||
* The string is parsed using {@link WrappedComponent#parseFromLegacy(String)}
|
||||
* to support Minecraft-style color codes and formatting.
|
||||
* The string is parsed using {@link WrappedComponent#parseFromLegacy(String)} to support Minecraft-style color codes and formatting.
|
||||
*
|
||||
* @param name the legacy text string to convert into an NPC name
|
||||
* @return a new {@link NpcName} representing the given legacy text
|
||||
* @return a new static {@link NpcName} representing the given legacy text
|
||||
*/
|
||||
public static @NotNull NpcName ofLegacy(@NotNull String name)
|
||||
{
|
||||
@@ -83,15 +108,15 @@ public class NpcName implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NpcName} with a dynamic function and a fallback name.
|
||||
* Creates a new {@link NpcName} with a dynamic function key and a fallback name.
|
||||
*
|
||||
* @param nameFunction the function producing the name for a given player
|
||||
* @param fallback the static fallback name component
|
||||
* @return a new NpcName instance
|
||||
* @param functionKey the registry key used to look up the function at runtime
|
||||
* @param fallback the static fallback name component
|
||||
* @return a new dynamic NpcName instance
|
||||
*/
|
||||
public static @NotNull NpcName of(@NotNull SerializableFunction<Player, WrappedComponent> nameFunction, @NotNull WrappedComponent fallback)
|
||||
public static @NotNull NpcName of(@NotNull String functionKey, @NotNull WrappedComponent fallback)
|
||||
{
|
||||
return new NpcName(nameFunction, fallback);
|
||||
return new NpcName(functionKey, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,13 +131,68 @@ public class NpcName implements Serializable
|
||||
return NpcName.of(WrappedComponent.create(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Serial
|
||||
private Object readResolve() throws ObjectStreamException
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
|
||||
{
|
||||
if(nameFunctionSerialized == null)
|
||||
return new NpcName(nameComponentSerialized.deserialize());
|
||||
ObjectInputStream.GetField fields;
|
||||
try
|
||||
{
|
||||
fields = in.readFields();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
this.nameComponent = WrappedComponent.create("Legacy NPC");
|
||||
this.nameFunctionKey = null;
|
||||
return;
|
||||
}
|
||||
|
||||
return new NpcName(player -> nameFunctionSerialized.apply(player).deserialize(), nameComponentSerialized.deserialize());
|
||||
this.nameComponentSerialized = (WrappedComponent.SerializedComponent) fields.get("nameComponentSerialized", null);
|
||||
this.nameComponent = this.nameComponentSerialized != null ? this.nameComponentSerialized.deserialize() : WrappedComponent.create(null);
|
||||
this.displayOptions = (NameDisplayOptions) fields.get("displayOptions", new NameDisplayOptions());
|
||||
|
||||
boolean isModernFormat = fields.getObjectStreamClass().getField("nameFunctionKey") != null;
|
||||
|
||||
if(isModernFormat)
|
||||
{
|
||||
this.nameFunctionKey = (String) fields.get("nameFunctionKey", null);
|
||||
return;
|
||||
}
|
||||
|
||||
SerializableFunction<Player, WrappedComponent.SerializedComponent> oldStringFunc;
|
||||
try
|
||||
{
|
||||
oldStringFunc = (SerializableFunction<Player, WrappedComponent.SerializedComponent>) fields.get("nameFunctionSerialized", null);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
oldStringFunc = null;
|
||||
}
|
||||
|
||||
if(oldStringFunc != null)
|
||||
this.nameFunctionKey = NpcRegistry.KEY_PLACEHOLDER_API;
|
||||
else
|
||||
this.nameFunctionKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display options for this NPC name.
|
||||
*
|
||||
* @return the display options
|
||||
*/
|
||||
public @NotNull NameDisplayOptions getDisplayOptions()
|
||||
{
|
||||
return this.displayOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display options for this NPC name.
|
||||
*
|
||||
* @param displayOptions the display options to set
|
||||
*/
|
||||
public void setDisplayOptions(@NotNull NameDisplayOptions displayOptions)
|
||||
{
|
||||
this.displayOptions = displayOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +202,7 @@ public class NpcName implements Serializable
|
||||
*/
|
||||
public boolean isStatic()
|
||||
{
|
||||
return nameFunction == null;
|
||||
return nameFunctionKey == null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,17 +216,29 @@ public class NpcName implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NPC name for a specific player.
|
||||
* Gets the contextual NPC name for a specific player, fallback to the static name if applicable.
|
||||
*
|
||||
* @param player the player to generate the name for
|
||||
* @return the name component for the player, or null if this is a static name and no function is defined
|
||||
* @param player the viewing player to evaluate the dynamic function for
|
||||
* @return the generated dynamic component, or the static fallback component if the name is static, the player is null, or function execution fails.
|
||||
*/
|
||||
public @Nullable WrappedComponent getName(@Nullable Player player)
|
||||
{
|
||||
if(nameFunction == null || player == null)
|
||||
return nameComponent;
|
||||
if(isStatic() || player == null)
|
||||
return getName();
|
||||
|
||||
return nameFunction.apply(player);
|
||||
BiFunction<Player, String, WrappedComponent> runtimeFunc = NpcRegistry.getNameFunction(nameFunctionKey.toLowerCase());
|
||||
if(runtimeFunc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return runtimeFunc.apply(player, nameComponent.toLegacy(true));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +248,9 @@ public class NpcName implements Serializable
|
||||
*/
|
||||
public @NotNull NpcName copy()
|
||||
{
|
||||
return isStatic() ? new NpcName(nameComponent) : new NpcName(nameFunction, nameComponent);
|
||||
NpcName copied = isStatic() ? new NpcName(nameComponent) : new NpcName(nameFunctionKey, nameComponent);
|
||||
copied.displayOptions = this.displayOptions.copy();
|
||||
return copied;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,4 +258,109 @@ public class NpcName implements Serializable
|
||||
{
|
||||
return "{" + (isStatic() ? "static" : "dynamic") + " -> " + getName().toLegacy(false) + "}";
|
||||
}
|
||||
|
||||
static class NpcNameAdapter implements JsonSerializer<NpcName>, JsonDeserializer<NpcName>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(NpcName src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if(src == null)
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
|
||||
if(src.getName() != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
String rawComponentJson = Var.toJson(src.getName());
|
||||
if(rawComponentJson != null && !rawComponentJson.isEmpty())
|
||||
obj.add("component", JsonParser.parseString(rawComponentJson));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
obj.add("component", JsonNull.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
if(src.nameFunctionKey != null)
|
||||
obj.addProperty("nameFunctionKey", src.nameFunctionKey);
|
||||
|
||||
if(src.getDisplayOptions() != null)
|
||||
obj.add("displayOptions", context.serialize(src.getDisplayOptions(), NameDisplayOptions.class));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpcName deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if(json == null || json.isJsonNull())
|
||||
return null;
|
||||
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
WrappedComponent component = null;
|
||||
|
||||
if(obj.has("component"))
|
||||
{
|
||||
JsonElement nameCompElement = obj.get("component");
|
||||
if(!nameCompElement.isJsonNull())
|
||||
component = Var.fromJson(nameCompElement.toString());
|
||||
}
|
||||
|
||||
if(component == null)
|
||||
component = WrappedComponent.create(null);
|
||||
|
||||
NpcName npcName;
|
||||
|
||||
if(obj.has("nameFunctionKey"))
|
||||
{
|
||||
String key = obj.get("nameFunctionKey").getAsString();
|
||||
npcName = NpcName.of(key, component);
|
||||
}
|
||||
else if(obj.has("nameFunctionSerialized"))
|
||||
{
|
||||
npcName = NpcName.of(component);
|
||||
try
|
||||
{
|
||||
Type funcType = new TypeToken<SerializableFunction<Player, WrappedComponent.SerializedComponent>>() {}.getType();
|
||||
SerializableFunction<Player, WrappedComponent.SerializedComponent> funcSerialized = context.deserialize(obj.get("nameFunctionSerialized"),
|
||||
funcType);
|
||||
|
||||
if(funcSerialized != null)
|
||||
{
|
||||
npcName.nameFunctionSerialized = funcSerialized;
|
||||
npcName.nameFunctionKey = NpcRegistry.KEY_PLACEHOLDER_API;
|
||||
npcName.nameFunction = player ->
|
||||
{
|
||||
try
|
||||
{
|
||||
WrappedComponent.SerializedComponent serializedComp = funcSerialized.apply(player);
|
||||
return serializedComp != null ? serializedComp.deserialize() : null;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
npcName.nameFunctionKey = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
npcName = NpcName.of(component);
|
||||
|
||||
if(obj.has("displayOptions"))
|
||||
{
|
||||
NameDisplayOptions options = context.deserialize(obj.get("displayOptions"), NameDisplayOptions.class);
|
||||
if(options != null)
|
||||
npcName.displayOptions = options;
|
||||
}
|
||||
|
||||
return npcName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
@@ -13,22 +14,25 @@ import de.eisi05.npc.api.manager.NpcManager;
|
||||
import de.eisi05.npc.api.manager.NpcVisibilityManager;
|
||||
import de.eisi05.npc.api.scheduler.Tasks;
|
||||
import de.eisi05.npc.api.utils.*;
|
||||
import de.eisi05.npc.api.utils.serialize.ItemSerializer;
|
||||
import de.eisi05.npc.api.wrapper.enums.ChatFormat;
|
||||
import de.eisi05.npc.api.wrapper.objects.*;
|
||||
import de.eisi05.npc.api.wrapper.packets.*;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
@@ -137,7 +141,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
|
||||
npc.getServerPlayer().getGameProfile().getProperties().put("textures", new Property("textures", skin.value(), skin.signature()));
|
||||
return null;
|
||||
}).loadBefore(!Versions.isCurrentVersionSmallerThan(Versions.V1_21_9));
|
||||
}).loadBefore(!Versions.isCurrentVersionSmallerThan(Versions.V1_21_9)).type(NpcSkin.class);
|
||||
|
||||
/**
|
||||
* NPC option to control whether the NPC is shown in the player tab list. If false, the NPC will be removed from the tab list for the viewing player after a
|
||||
@@ -154,10 +158,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_19_3) && player != null)
|
||||
WrappedServerPlayer.fromPlayer(player).sendPacket(
|
||||
new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.REMOVE_PLAYER, npc.getServerPlayer()));
|
||||
else if(player != null && npc.getUUID() != null)
|
||||
if(player != null && npc.getUUID() != null)
|
||||
WrappedServerPlayer.fromPlayer(player).sendPacket(new PlayerInfoRemovePacket(List.of(npc.getUUID())));
|
||||
}
|
||||
}.runTaskLater(NpcApi.plugin, 50);
|
||||
@@ -171,10 +172,8 @@ public class NpcOption<T, S extends Serializable>
|
||||
public static final NpcOption<Integer, Integer> LATENCY = new NpcOption<>("latency", () -> 0,
|
||||
aInteger -> aInteger, aInteger -> aInteger, aInteger -> aInteger,
|
||||
(latency, npc, player) ->
|
||||
{
|
||||
npc.getServerPlayer().setLatency(latency);
|
||||
return new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY, npc.getServerPlayer());
|
||||
});
|
||||
new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY, npc.getServerPlayer()))
|
||||
.type(TypeToken.of(Integer.class).getType());
|
||||
|
||||
/**
|
||||
* NPC option to control the visibility of the NPC's nametag.
|
||||
@@ -184,48 +183,33 @@ public class NpcOption<T, S extends Serializable>
|
||||
(hide, npc, player) ->
|
||||
{
|
||||
WrappedEntity<?> nameTag = npc.getServerPlayer().getNameTag();
|
||||
|
||||
WrappedEntityData data = npc.getServerPlayer().getNameTag().getEntityData();
|
||||
if(nameTag instanceof WrappedArmorStand)
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(3), !hide);
|
||||
else if(nameTag instanceof WrappedTextDisplay)
|
||||
{
|
||||
if(!hide)
|
||||
return null;
|
||||
|
||||
if(nameTag instanceof WrappedTextDisplay && hide)
|
||||
return new RemoveEntityPacket(npc.getServerPlayer().getNameTag().getId());
|
||||
}
|
||||
|
||||
return SetEntityDataPacket.create(npc.getServerPlayer().getNameTag().getId(), data);
|
||||
return null;
|
||||
});
|
||||
|
||||
/**
|
||||
* NPC option to set the equipment worn by the NPC (armor, items in hand). The map uses {@link EquipmentSlot} as keys and {@link ItemStack} as values.
|
||||
* Serialized form uses item base64 strings.
|
||||
*/
|
||||
public static final NpcOption<Map<EquipmentSlot, ItemStack>, HashMap<EquipmentSlot, String>> EQUIPMENT = new NpcOption<>("equipment", HashMap::new, HashMap::new,
|
||||
map ->
|
||||
{
|
||||
HashMap<EquipmentSlot, String> serializedMap = new HashMap<>();
|
||||
map.forEach((slot, item) ->
|
||||
{
|
||||
if(item == null || item.getType().isAir())
|
||||
return;
|
||||
|
||||
String serialized = ItemSerializer.itemStackToBase64(item);
|
||||
if(serialized == null)
|
||||
return;
|
||||
|
||||
serializedMap.put(slot, serialized);
|
||||
});
|
||||
return serializedMap;
|
||||
},
|
||||
public static final NpcOption<Map<EquipmentSlot, ItemStack>, HashMap<EquipmentSlot, ?>> EQUIPMENT = new NpcOption<Map<EquipmentSlot, ItemStack>, HashMap<EquipmentSlot, ?>>("equipment", HashMap::new,
|
||||
HashMap::new, HashMap::new,
|
||||
serializedMap ->
|
||||
{
|
||||
HashMap<EquipmentSlot, ItemStack> map = new HashMap<>();
|
||||
serializedMap.forEach((slot, string) ->
|
||||
serializedMap.forEach((slot, object) ->
|
||||
{
|
||||
if(string == null || string.isEmpty())
|
||||
if(object == null)
|
||||
return;
|
||||
|
||||
if(object instanceof ItemStack)
|
||||
{
|
||||
map.put(slot, (ItemStack) object);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!(object instanceof String string) || string.isEmpty())
|
||||
return;
|
||||
|
||||
ItemStack item = ItemSerializer.itemStackFromBase64(string);
|
||||
@@ -247,7 +231,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
new Pair<>(de.eisi05.npc.api.wrapper.enums.EquipmentSlot.values()[slot.ordinal()].getHandle(), Var.toNmsItemStack(item))));
|
||||
|
||||
return new SetEquipmentPacket(npc.entity.getId(), list);
|
||||
});
|
||||
}).type(new TypeToken<HashMap<EquipmentSlot, ItemStack>>(){}.getType());
|
||||
|
||||
/**
|
||||
* NPC option to control which parts of the NPC's skin are visible (e.g., hat, jacket). For a full list look at {@link SkinParts}.
|
||||
@@ -260,7 +244,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BYTE.create(Versions.isCurrentVersionSmallerThan(Versions.V1_21_9) ? 17 : 16),
|
||||
(byte) Arrays.stream(skinParts).mapToInt(SkinParts::getValue).sum());
|
||||
return SetEntityDataPacket.create(npc.getServerPlayer().getId(), data);
|
||||
});
|
||||
}).type(SkinParts[].class);
|
||||
|
||||
/**
|
||||
* NPC option to make the NPC look at the player if they are within a certain distance. The value is the maximum distance in blocks. A value of 0 or less
|
||||
@@ -268,7 +252,8 @@ public class NpcOption<T, S extends Serializable>
|
||||
*/
|
||||
public static final NpcOption<Double, Double> LOOK_AT_PLAYER = new NpcOption<>("look-at-player", () -> 0.0,
|
||||
distance -> distance, distance -> distance, distance -> distance,
|
||||
(distance, npc, player) -> null);
|
||||
(distance, npc, player) -> null)
|
||||
.type(TypeToken.of(Double.class).getType());
|
||||
|
||||
/**
|
||||
* NPC option to control visibility with three states: fully visible, transparent, or invisible.
|
||||
@@ -328,7 +313,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
var addPlayer = SetPlayerTeamPacket.createPlayerPacket(wrappedPlayerTeam, player.getName(), SetPlayerTeamPacket.Action.ADD);
|
||||
|
||||
return new BundlePacket(teamPacket, SetEntityDataPacket.create(npc.entity.getId(), entityData), addNpc, addPlayer);
|
||||
});
|
||||
}).type(NpcVisibility.class);
|
||||
|
||||
/**
|
||||
* NPC option to make the NPC glow with a specific color. If null, the glowing effect is removed.
|
||||
@@ -339,7 +324,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public static final NpcOption<ChatColor, ChatColor> GLOWING = new NpcOption<>("glowing", () -> null,
|
||||
public static final NpcOption<ChatColor, ChatColor> GLOWING = new NpcOption<ChatColor, ChatColor>("glowing", () -> null,
|
||||
color -> color, color -> color, color -> color,
|
||||
(color, npc, player) ->
|
||||
{
|
||||
@@ -366,7 +351,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
entityData.set(accessor, (byte) (flags | modifier));
|
||||
|
||||
return new BundlePacket(teamPacket, SetEntityDataPacket.create(npc.entity.getId(), entityData));
|
||||
});
|
||||
}).type(ChatColor.class);
|
||||
|
||||
/**
|
||||
* NPC option to control collision behavior between players and NPCs. When enabled, players can push and collide with NPCs. When disabled, players pass
|
||||
@@ -468,12 +453,13 @@ public class NpcOption<T, S extends Serializable>
|
||||
}.runTaskLater(NpcApi.plugin, 1);
|
||||
}
|
||||
|
||||
Integer oldId = npc.toDeleteEntities.remove("sit");
|
||||
Map<String, Integer> playerEntities = npc.toDeleteEntities.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>());
|
||||
Integer oldId = playerEntities.remove("sit");
|
||||
if(nmsPose == de.eisi05.npc.api.wrapper.enums.Pose.SITTING)
|
||||
{
|
||||
WrappedTextDisplay textDisplay = WrappedTextDisplay.create(npc.getLocation().getWorld());
|
||||
textDisplay.moveTo(npc.getLocation());
|
||||
npc.toDeleteEntities.put("sit", textDisplay.getId());
|
||||
playerEntities.put("sit", textDisplay.getId());
|
||||
|
||||
PacketWrapper addEntityPacket = textDisplay.getAddEntityPacket();
|
||||
|
||||
@@ -501,7 +487,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
new BundlePacket(new RemoveEntityPacket(oldId), SetEntityDataPacket.create(npc.entity.getId(), data)) :
|
||||
new BundlePacket(packetWrapper, new RemoveEntityPacket(oldId), SetEntityDataPacket.create(npc.entity.getId(), data));
|
||||
}
|
||||
});
|
||||
}).type(Pose.class);
|
||||
|
||||
/**
|
||||
* NPC option to set the scale (size) of the NPC. A value of 1.0 is normal size. Requires Minecraft 1.20.6 or newer.
|
||||
@@ -518,7 +504,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
instance.setBaseValue(scale);
|
||||
|
||||
return new UpdateAttributesPacket(npc.entity.getId(), instance);
|
||||
}).since(Versions.V1_20_6);
|
||||
}).since(Versions.V1_20_6).type(TypeToken.of(Double.class).getType());
|
||||
|
||||
/**
|
||||
* NPC option to control the position of the NPC in the TAB list.
|
||||
@@ -536,7 +522,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
npc.getServerPlayer().setListOrder(order);
|
||||
|
||||
return new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER, npc.getServerPlayer());
|
||||
}).since(Versions.V1_21_2);
|
||||
}).since(Versions.V1_21_2).type(TypeToken.of(Integer.class).getType());
|
||||
|
||||
/**
|
||||
* NPC option to change the entity type of the NPC. This allows transforming the NPC into any Minecraft entity type. The default is a PLAYER entity. When
|
||||
@@ -564,17 +550,21 @@ public class NpcOption<T, S extends Serializable>
|
||||
{
|
||||
entity = wrappedEntitySnapshot.create(player.getWorld());
|
||||
entity.moveTo(npc.getLocation());
|
||||
npc.toDeleteEntities.put("entity", entity.getId());
|
||||
npc.toDeleteEntities.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()).put("entity", entity.getId());
|
||||
}
|
||||
else
|
||||
entity = npc.entity;
|
||||
|
||||
npc.entity = entity;
|
||||
if(entity instanceof WrappedEnderDragon dragon)
|
||||
dragon.getSubIds().forEach(id -> NpcManager.addID(id, npc));
|
||||
|
||||
NpcManager.addID(npc.entity.getId(), npc);
|
||||
|
||||
List<PacketWrapper> packets = new ArrayList<>();
|
||||
|
||||
packets.add(new RemoveEntityPacket(npc.getServerPlayer().getId()));
|
||||
packets.add(new RemoveEntityPacket(npc.entity.getId()));
|
||||
packets.add(entity.getAddEntityPacket());
|
||||
|
||||
var teamPair = getTeam(player, npc);
|
||||
@@ -598,9 +588,53 @@ public class NpcOption<T, S extends Serializable>
|
||||
(byte) (Var.nbtToEntityFlags(wrappedEntitySnapshot.getData()) | Var.extractFlagsFromBukkit(entity.getBukkitPlayer())));
|
||||
data.set(WrappedEntityData.EntityDataSerializers.OPTIONAL_CHAT_COMPONENT.create(2), Optional.of(WrappedComponent.create("NPC").getHandle()));
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(3), false);
|
||||
packets.add(SetEntityDataPacket.create(entity.getId(), data));
|
||||
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_19_4) || !npc.getOption(NpcOption.HIDE_NAMETAG, player))
|
||||
if(entity.getBukkitPlayer() instanceof BlockDisplay display)
|
||||
{
|
||||
Vector3f translation = display.getTransformation().getTranslation();
|
||||
if(!display.getPersistentDataContainer().has(NpcApi.displayKey, PersistentDataType.BOOLEAN))
|
||||
{
|
||||
display.getPersistentDataContainer().set(NpcApi.displayKey, PersistentDataType.BOOLEAN, true);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(11),
|
||||
new Vector3f(translation.x - 0.5f, translation.y, translation.z - 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
if(entity.getBukkitPlayer() instanceof ItemDisplay display)
|
||||
{
|
||||
Vector3f translation = display.getTransformation().getTranslation();
|
||||
if(!display.getPersistentDataContainer().has(NpcApi.displayKey, PersistentDataType.BOOLEAN))
|
||||
{
|
||||
display.getPersistentDataContainer().set(NpcApi.displayKey, PersistentDataType.BOOLEAN, true);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(11), new Vector3f(translation.x, translation.y + 0.5f, translation.z));
|
||||
}
|
||||
}
|
||||
|
||||
packets.add(SetEntityDataPacket.createNoneDefaults(entity.getId(), data));
|
||||
|
||||
List<WrappedEntity<?>> passengers = new ArrayList<>();
|
||||
if(entity.getBukkitPlayer() instanceof Display display)
|
||||
{
|
||||
Vector3f scale = display.getTransformation().getScale();
|
||||
|
||||
WrappedInteraction interaction = WrappedInteraction.create(npc.getLocation().getWorld());
|
||||
interaction.moveTo(npc.getLocation());
|
||||
float width = Math.max(scale.x, scale.z);
|
||||
float height = scale.y;
|
||||
|
||||
NpcManager.addID(interaction.getId(), npc);
|
||||
npc.toDeleteEntities.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()).put("interaction", interaction.getId());
|
||||
passengers.add(interaction);
|
||||
|
||||
npc.name.getDisplayOptions().setHeight(height);
|
||||
WrappedEntityData displayData = interaction.getData(width, height);
|
||||
packets.add(interaction.getAddEntityPacket());
|
||||
packets.add(SetEntityDataPacket.create(interaction.getId(), displayData));
|
||||
}
|
||||
else
|
||||
npc.name.getDisplayOptions().setHeight(0);
|
||||
|
||||
if(!npc.getOption(NpcOption.HIDE_NAMETAG, player))
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_21))
|
||||
npc.serverPlayer.getNameTag()
|
||||
@@ -610,19 +644,29 @@ public class NpcOption<T, S extends Serializable>
|
||||
packets.add(npc.serverPlayer.getNameTag().getAddEntityPacket());
|
||||
|
||||
packets.add(SetEntityDataPacket.create(npc.serverPlayer.getNameTag().getId(), npc.serverPlayer.getNameTag().applyData(
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V1_19_4) || npc.isEnabled() ? npc.name.getName(player) :
|
||||
npc.isEnabled() ? npc.name.getName(player) :
|
||||
WrappedComponent.parseFromLegacy(NpcApi.DISABLED_MESSAGE_PROVIDER.apply(player))
|
||||
.append(WrappedComponent.create("\n").append(npc.name.getName(player))))));
|
||||
.append(WrappedComponent.create("\n").append(npc.name.getName(player))), npc.name.getDisplayOptions())));
|
||||
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V1_19_4))
|
||||
{
|
||||
entity.setPassengers(npc.serverPlayer.getNameTag());
|
||||
packets.add(new SetPassengerPacket(entity));
|
||||
}
|
||||
passengers.add(npc.serverPlayer.getNameTag());
|
||||
}
|
||||
|
||||
if(!passengers.isEmpty())
|
||||
{
|
||||
entity.setPassengers(passengers.toArray(new WrappedEntity<?>[0]));
|
||||
packets.add(new SetPassengerPacket(entity));
|
||||
}
|
||||
|
||||
return new BundlePacket(packets.toArray(new PacketWrapper[0]));
|
||||
}).loadBefore(true);
|
||||
}).loadBefore(true).type(WrappedEntitySnapshot.class);
|
||||
|
||||
/**
|
||||
* NPC option to store custom tags for referencing and categorizing NPCs.
|
||||
*/
|
||||
static final NpcOption<Set<String>, HashSet<String>> TAGS = new NpcOption<Set<String>, HashSet<String>>("tags", HashSet::new,
|
||||
HashSet::new, HashSet::new, HashSet::new,
|
||||
(tags, npc, player) -> null)
|
||||
.type(new TypeToken<HashSet<String>>() {}.getType());
|
||||
|
||||
/**
|
||||
* NPC option to control if the NPC is enabled (visible and interactable). If false, a "DISABLED" marker may be shown. This is an internal option, typically
|
||||
@@ -630,33 +674,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
*/
|
||||
static final NpcOption<Boolean, Boolean> ENABLED = new NpcOption<>("enabled", () -> false,
|
||||
aBoolean -> aBoolean, aBoolean -> aBoolean, aBoolean -> aBoolean,
|
||||
(enabled, npc, player) ->
|
||||
{
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V1_19_4))
|
||||
return null;
|
||||
|
||||
if(enabled)
|
||||
return null;
|
||||
|
||||
WrappedArmorStand armorStand = WrappedArmorStand.create(npc.getLocation().getWorld());
|
||||
armorStand.moveTo(npc.getLocation().clone()
|
||||
.add(0, (npc.entity.getBoundingBox().getYSize() * npc.getOption(SCALE, player)), 0));
|
||||
|
||||
PacketWrapper addPacket = armorStand.getAddEntityPacket();
|
||||
|
||||
WrappedEntityData data = armorStand.getEntityData();
|
||||
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BYTE.create(0), (byte) 0x20);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.OPTIONAL_CHAT_COMPONENT.create(2),
|
||||
Optional.of(WrappedComponent.parseFromLegacy(NpcApi.DISABLED_MESSAGE_PROVIDER.apply(player)).getHandle()));
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(3), true);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(4), true);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BYTE.create(15), (byte) 0x10);
|
||||
|
||||
npc.toDeleteEntities.put("name", armorStand.getId());
|
||||
|
||||
return new BundlePacket(addPacket, SetEntityDataPacket.create(armorStand.getId(), data));
|
||||
});
|
||||
(enabled, npc, player) -> null);
|
||||
|
||||
/**
|
||||
* NPC option to control if the NPC is enabled (visible and interactable). If false, a "DISABLED" marker may be shown. This is an internal option, typically
|
||||
@@ -671,19 +689,22 @@ public class NpcOption<T, S extends Serializable>
|
||||
* tick interval, and goal configurations. Note: Goals themselves are not fully serialized - only their class names and any serializable configuration. On
|
||||
* deserialization, goals must be re-instantiated by the plugin.
|
||||
*/
|
||||
static final NpcOption<ArrayList<Goal>, ArrayList<Goal>> GOALS = new NpcOption<>("goals", ArrayList::new,
|
||||
static final NpcOption<ArrayList<Goal>, ArrayList<Goal>> GOALS = new NpcOption<ArrayList<Goal>, ArrayList<Goal>>("goals", ArrayList::new,
|
||||
goals -> new ArrayList<>(goals.stream().map(Goal::copy).toList()), goals -> goals,
|
||||
goals -> goals,
|
||||
(data, npc, player) -> null);
|
||||
(data, npc, player) -> null)
|
||||
.type(new TypeToken<ArrayList<Goal>>(){}.getType());
|
||||
|
||||
/**
|
||||
* NPC option to store custom data for the NPC. This is an internal option, typically not directly set by users but controlled by
|
||||
* {@link NPC#addCustomData(Serializable, Serializable)}.
|
||||
*/
|
||||
static final NpcOption<HashMap<Serializable, Serializable>, HashMap<Serializable, Serializable>> CUSTOM_DATA = new NpcOption<>("custom-data",
|
||||
static final NpcOption<HashMap<Serializable, Serializable>, HashMap<Serializable, Serializable>> CUSTOM_DATA =
|
||||
new NpcOption<HashMap<Serializable, Serializable>, HashMap<Serializable, Serializable>>("custom-data",
|
||||
HashMap::new, HashMap::new,
|
||||
aHashMap -> aHashMap, aHashMap -> aHashMap,
|
||||
(customData, npc, player) -> null);
|
||||
(customData, npc, player) -> null)
|
||||
.type(new TypeToken<HashMap<Serializable, Serializable>>() {}.getType());
|
||||
|
||||
/**
|
||||
* NPC option to manage visibility settings for the NPC. This controls whether the NPC should be shown to all players (including new ones) or only to specific players.
|
||||
@@ -702,7 +723,7 @@ public class NpcOption<T, S extends Serializable>
|
||||
|
||||
return visibilityManager;
|
||||
},
|
||||
(visibilityManager, npc, player) -> null);
|
||||
(visibilityManager, npc, player) -> null).type(NpcVisibilityManager.class);
|
||||
|
||||
private final String path;
|
||||
private final Supplier<T> defaultValue;
|
||||
@@ -710,8 +731,9 @@ public class NpcOption<T, S extends Serializable>
|
||||
private final Function<S, T> deserializer;
|
||||
private final Function<T, T> copyFunction;
|
||||
private final TriFunction<T, NPC, Player, PacketWrapper> packet;
|
||||
private Versions since = Versions.V1_17;
|
||||
private Versions since = Versions.V1_20_6;
|
||||
private boolean loadBefore = false;
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* Private constructor to create a new NpcOption.
|
||||
@@ -792,6 +814,12 @@ public class NpcOption<T, S extends Serializable>
|
||||
return this;
|
||||
}
|
||||
|
||||
private NpcOption<T, S> type(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this option should be loaded before other options.
|
||||
*
|
||||
@@ -894,25 +922,36 @@ public class NpcOption<T, S extends Serializable>
|
||||
*/
|
||||
public @Nullable T deserialize(@Nullable S var1)
|
||||
{
|
||||
return deserializer.apply(var1);
|
||||
try
|
||||
{
|
||||
return deserializer.apply(var1);
|
||||
}
|
||||
catch(ClassCastException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public @Nullable Type getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the network packet(s) needed to apply this option's value to an NPC for a specific player. The method checks for version compatibility before
|
||||
* generating the packet.
|
||||
*
|
||||
* @param object The value of the option to apply. Can be null.
|
||||
* @param npc The {@link NPC} to apply the option to. Must not be null.
|
||||
* @param player The {@link Player} who will receive the update. Must not be null.
|
||||
* @return An {@link Optional} containing the {@link PacketWrapper} if one is generated and the option is compatible, otherwise an empty Optional.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NotNull Optional<PacketWrapper> getPacket(@Nullable Object object, @NotNull NPC npc, @NotNull Player player)
|
||||
public @NotNull Optional<PacketWrapper> getPacket(@NotNull NPC npc, @NotNull Player player)
|
||||
{
|
||||
if(packet == null || !isCompatible())
|
||||
return Optional.empty();
|
||||
|
||||
return Optional.ofNullable(packet.apply((T) object, npc, player));
|
||||
return Optional.ofNullable(packet.apply(npc.getOption(this, player), npc, player));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package de.eisi05.npc.api.objects;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import de.eisi05.npc.api.scheduler.Tasks;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.SerializableBiFunction;
|
||||
import de.eisi05.npc.api.utils.TriFunction;
|
||||
import de.eisi05.npc.api.utils.serialize.NpcRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -19,38 +23,73 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* An {@code NpcSkin} can be:
|
||||
* <ul>
|
||||
* <li><b>Static</b> — using a fixed {@link Skin}.</li>
|
||||
* <li><b>Dynamic</b> — using a {@link TriFunction} that calculates
|
||||
* a {@link Skin} based on a {@link Player}, {@link NPC}, and a placeholder string.</li>
|
||||
* <li><b>Dynamic</b> — using a registered function key that resolves a {@link Skin}
|
||||
* based on a {@link Player}, {@link NPC}, and placeholder string.</li>
|
||||
* <li><b>Placeholder-based</b> — using PlaceholderAPI to resolve skins from placeholders.</li>
|
||||
* </ul>
|
||||
* This class is serializable, allowing NPCs to persist their skin information.
|
||||
* </p>
|
||||
*/
|
||||
@JsonAdapter(NpcSkin.NpcSkinAdapter.class)
|
||||
@SuppressWarnings({"Convert2Lambda", "Convert2Diamond"})
|
||||
public class NpcSkin implements SkinData
|
||||
{
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Skin skin;
|
||||
static
|
||||
{
|
||||
// Needs to be an anonymous class to avoid lambda deserialization issues
|
||||
NpcRegistry.registerSkinFunction(NpcRegistry.KEY_PLACEHOLDER_API, new TriFunction<Player, NPC, String, Skin>()
|
||||
{
|
||||
@Override
|
||||
public Skin apply(Player player, NPC npc, String placeholder)
|
||||
{
|
||||
if(!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
return null;
|
||||
|
||||
@Deprecated(since = "1.4.5")
|
||||
private final SerializableBiFunction<Player, NPC, Skin> skinFunction;
|
||||
String newPlaceholder = Tasks.placeholderCache
|
||||
.computeIfAbsent(npc.getUUID(), k -> new ConcurrentHashMap<>())
|
||||
.get(player.getUniqueId());
|
||||
|
||||
if(newPlaceholder == null)
|
||||
newPlaceholder = (String) Reflections.invokeStaticMethod("me.clip.placeholderapi.PlaceholderAPI", "setPlaceholders", player, placeholder).get();
|
||||
|
||||
if(newPlaceholder == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
UUID uuid = UUID.fromString(newPlaceholder);
|
||||
if(Skin.isPreLoaded(uuid))
|
||||
return Skin.fetchSkin(uuid).orElse(null);
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
if(Skin.isPreLoaded(newPlaceholder))
|
||||
return Skin.fetchSkin(newPlaceholder).orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final Skin skin;
|
||||
private final String placeholder;
|
||||
private TriFunction<Player, NPC, String, Skin> newSkinFunction;
|
||||
private final String skinFunctionKey;
|
||||
|
||||
/**
|
||||
* Creates a dynamic NPC skin that uses a function to determine the skin.
|
||||
* Creates a dynamic NPC skin using a registered function key.
|
||||
*
|
||||
* @param skinFunction the function that generates a skin based on player, NPC, and placeholder string.
|
||||
* @param placeholder the placeholder string to be used for skin resolution.
|
||||
* @param fallback the fallback {@link Skin} to use if the function returns {@code null}.
|
||||
* @param skinFunctionKey the registry key corresponding to a registered skin function
|
||||
* @param placeholder the placeholder string evaluated by the function
|
||||
* @param fallback the fallback {@link Skin} if resolution fails or returns {@code null}
|
||||
*/
|
||||
private NpcSkin(@NotNull TriFunction<Player, NPC, String, Skin> skinFunction, @NotNull String placeholder, @Nullable Skin fallback)
|
||||
private NpcSkin(@NotNull String skinFunctionKey, @NotNull String placeholder, @Nullable Skin fallback)
|
||||
{
|
||||
this.skin = fallback;
|
||||
this.newSkinFunction = skinFunction;
|
||||
this.skinFunctionKey = skinFunctionKey;
|
||||
this.placeholder = placeholder;
|
||||
this.skinFunction = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,8 +100,7 @@ public class NpcSkin implements SkinData
|
||||
private NpcSkin(@NotNull Skin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
this.skinFunction = null;
|
||||
this.newSkinFunction = null;
|
||||
this.skinFunctionKey = null;
|
||||
this.placeholder = null;
|
||||
}
|
||||
|
||||
@@ -78,15 +116,16 @@ public class NpcSkin implements SkinData
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dynamic {@link NpcSkin} that determines the skin at runtime.
|
||||
* Creates a new dynamic {@link NpcSkin} that determines the skin at runtime using a registered registry key.
|
||||
*
|
||||
* @param skinFunction the function used to calculate the {@link Skin}.
|
||||
* @param fallback the fallback skin if the function fails or returns {@code null}.
|
||||
* @return a new dynamic {@code NpcSkin}.
|
||||
* @param skinFunctionKey the registry key identifying the skin function to evaluate
|
||||
* @param placeholder the placeholder string passed to the function
|
||||
* @param fallback the fallback skin if the function fails or returns {@code null}
|
||||
* @return a new dynamic {@code NpcSkin}
|
||||
*/
|
||||
public static @NotNull NpcSkin of(@NotNull TriFunction<Player, NPC, String, Skin> skinFunction, @NotNull String placeholder, @Nullable Skin fallback)
|
||||
public static @NotNull NpcSkin of(@NotNull String skinFunctionKey, @NotNull String placeholder, @Nullable Skin fallback)
|
||||
{
|
||||
return new NpcSkin(skinFunction, placeholder, fallback);
|
||||
return new NpcSkin(skinFunctionKey, placeholder, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,38 +159,30 @@ public class NpcSkin implements SkinData
|
||||
*/
|
||||
public static @NotNull NpcSkin ofPlaceholderAPI(@NotNull String placeholder, @Nullable Skin fallback)
|
||||
{
|
||||
return of((player, npc, s) ->
|
||||
{
|
||||
String newPlaceholder = Tasks.placeholderCache.computeIfAbsent(npc.getUUID(), k -> new ConcurrentHashMap<>()).get(player.getUniqueId());
|
||||
if(newPlaceholder == null)
|
||||
newPlaceholder = (String) Reflections.invokeStaticMethod("me.clip.placeholderapi.PlaceholderAPI", "setPlaceholders", player, s).get();
|
||||
|
||||
if(newPlaceholder == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
UUID uuid = UUID.fromString(newPlaceholder);
|
||||
|
||||
if(Skin.isPreLoaded(uuid))
|
||||
return Skin.fetchSkin(uuid).orElse(null);
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
if(Skin.isPreLoaded(newPlaceholder))
|
||||
return Skin.fetchSkin(newPlaceholder).orElse(null);
|
||||
}
|
||||
return null;
|
||||
}, placeholder, fallback);
|
||||
return new NpcSkin(NpcRegistry.KEY_PLACEHOLDER_API, placeholder, fallback);
|
||||
}
|
||||
|
||||
@Serial
|
||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
|
||||
/**
|
||||
* Hijacks Java's internal lambda deserialization to safely bypass old lambda crashes.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static Object $deserializeLambda$(java.lang.invoke.SerializedLambda lambda)
|
||||
{
|
||||
in.defaultReadObject();
|
||||
String funcInterface = lambda.getFunctionalInterfaceClass();
|
||||
if (funcInterface != null && funcInterface.contains("SerializableBiFunction"))
|
||||
{
|
||||
return new SerializableBiFunction<Player, NPC, Skin>()
|
||||
{
|
||||
@Override
|
||||
public Skin apply(Player p, NPC n) { return null; }
|
||||
};
|
||||
}
|
||||
|
||||
if(newSkinFunction == null)
|
||||
newSkinFunction = ((player, npc, s) -> skinFunction != null ? skinFunction.apply(player, npc) : null);
|
||||
return new TriFunction<Player, NPC, String, Skin>()
|
||||
{
|
||||
@Override
|
||||
public Skin apply(Player p, NPC n, String s) { return null; }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,20 +198,32 @@ public class NpcSkin implements SkinData
|
||||
/**
|
||||
* Gets the skin for a specific player and NPC.
|
||||
* <p>
|
||||
* If this is a dynamic skin, the result of the {@link TriFunction} is returned. The function receives the player, NPC, and placeholder string as
|
||||
* If this is a dynamic skin, the result of the registered {@link TriFunction} is returned. The function receives the player, NPC, and placeholder string as
|
||||
* parameters. If the function returns null or this is a static skin, the fallback skin is used.
|
||||
* </p>
|
||||
*
|
||||
* @param player the player for whom to get the skin.
|
||||
* @param npc the NPC whose skin is being retrieved.
|
||||
* @return the resolved {@link Skin}, or the fallback skin if unavailable, or {@code null} if no fallback exists.
|
||||
* @param player the player for whom to get the skin
|
||||
* @param npc the NPC whose skin is being retrieved
|
||||
* @return the resolved {@link Skin}, or the fallback skin if unavailable, or {@code null} if no fallback exists
|
||||
*/
|
||||
public @Nullable Skin getSkin(@NotNull Player player, @NotNull NPC npc)
|
||||
{
|
||||
Skin skin = null;
|
||||
if(newSkinFunction != null)
|
||||
skin = newSkinFunction.apply(player, npc, placeholder);
|
||||
return skin != null ? skin : getSkin();
|
||||
if(isStatic())
|
||||
return getSkin();
|
||||
|
||||
TriFunction<Player, NPC, String, Skin> function = NpcRegistry.getSkinFunction(skinFunctionKey.toLowerCase());
|
||||
if(function != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Skin resolved = function.apply(player, npc, placeholder);
|
||||
if(resolved != null)
|
||||
return resolved;
|
||||
}
|
||||
catch(Exception ignored) {}
|
||||
}
|
||||
|
||||
return getSkin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +243,17 @@ public class NpcSkin implements SkinData
|
||||
*/
|
||||
public boolean isStatic()
|
||||
{
|
||||
return newSkinFunction == null;
|
||||
return skinFunctionKey == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registry key identifying the skin function to evaluate.
|
||||
*
|
||||
* @return the registry key, or {@code null} if this is a static skin.
|
||||
*/
|
||||
public @Nullable String getSkinFunctionKey()
|
||||
{
|
||||
return skinFunctionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +263,7 @@ public class NpcSkin implements SkinData
|
||||
*/
|
||||
public @NotNull NpcSkin copy()
|
||||
{
|
||||
return isStatic() ? new NpcSkin(skin) : new NpcSkin(newSkinFunction, placeholder, skin);
|
||||
return isStatic() ? new NpcSkin(skin) : new NpcSkin(skinFunctionKey, placeholder, skin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,4 +276,87 @@ public class NpcSkin implements SkinData
|
||||
{
|
||||
return "{" + (isStatic() ? "static" : "dynamic") + " -> " + skin + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gson JSON adapter for serializing and deserializing {@link NpcSkin} objects.
|
||||
*/
|
||||
static class NpcSkinAdapter implements JsonSerializer<NpcSkin>, JsonDeserializer<NpcSkin>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(NpcSkin src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if(src == null)
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
|
||||
if(src.getSkin() != null)
|
||||
{
|
||||
JsonElement skinElem = context.serialize(src.getSkin());
|
||||
if(skinElem != null && skinElem.isJsonObject())
|
||||
{
|
||||
for(var entry : skinElem.getAsJsonObject().entrySet())
|
||||
obj.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if(src.skinFunctionKey != null)
|
||||
obj.addProperty(src.skinFunctionKey, src.getPlaceholder() != null ? src.getPlaceholder() : "");
|
||||
else if(src.getPlaceholder() != null)
|
||||
obj.addProperty(NpcRegistry.KEY_PLACEHOLDER_API, src.getPlaceholder());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpcSkin deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if(json == null || json.isJsonNull())
|
||||
return null;
|
||||
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
|
||||
Skin fallback = null;
|
||||
if(obj.has("fallbackSkin"))
|
||||
fallback = context.deserialize(obj.get("fallbackSkin"), Skin.class);
|
||||
else if(obj.has("skin"))
|
||||
fallback = context.deserialize(obj.get("skin"), Skin.class);
|
||||
else if(obj.has("value") || obj.has("signature"))
|
||||
fallback = context.deserialize(obj, Skin.class);
|
||||
|
||||
String functionKey = null;
|
||||
String placeholder = null;
|
||||
|
||||
if(obj.has("skinFunctionKey"))
|
||||
{
|
||||
functionKey = obj.get("skinFunctionKey").getAsString();
|
||||
placeholder = obj.has("placeholder") ? obj.get("placeholder").getAsString() : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(var entry : obj.entrySet())
|
||||
{
|
||||
String key = entry.getKey();
|
||||
if(key.equals("fallbackSkin") || key.equals("skin") || key.equals("value") || key.equals("signature"))
|
||||
continue;
|
||||
|
||||
functionKey = key;
|
||||
JsonElement val = entry.getValue();
|
||||
placeholder = (val != null && !val.isJsonNull() && !val.getAsString().isEmpty()) ? val.getAsString() : null;
|
||||
break;
|
||||
}
|
||||
|
||||
if(functionKey == null && obj.has("placeholder"))
|
||||
placeholder = obj.get("placeholder").getAsString();
|
||||
}
|
||||
|
||||
if(functionKey == null && placeholder != null)
|
||||
functionKey = NpcRegistry.KEY_PLACEHOLDER_API;
|
||||
|
||||
if(functionKey != null)
|
||||
return NpcSkin.of(functionKey, placeholder, fallback);
|
||||
|
||||
return NpcSkin.of(fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
private static final Map<String, Skin> skinCacheName = new ConcurrentHashMap<>();
|
||||
private static final Map<UUID, Skin> skinCacheUUID = new ConcurrentHashMap<>();
|
||||
private static final Map<File, Skin> skinCacheFile = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Skin> skinCacheUrl = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Retrieves the skin data directly from a currently online Bukkit player. This method uses reflection to access the player's game profile properties.
|
||||
@@ -70,11 +71,8 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
if(Versions.getVersion() == Versions.NONE)
|
||||
throw new VersionNotFound();
|
||||
|
||||
return Versions.isCurrentVersionSmallerThan(Versions.V1_20_2) ?
|
||||
new Skin(player.getName(), Reflections.<String>invokeMethod(property, "getValue").get(),
|
||||
Reflections.<String>invokeMethod(property, "getSignature").get()) :
|
||||
new Skin(player.getName(), Reflections.<String>invokeMethod(property, "value").get(),
|
||||
Reflections.<String>invokeMethod(property, "signature").get());
|
||||
return new Skin(player.getName(), Reflections.<String>invokeMethod(property, "value").get(),
|
||||
Reflections.<String>invokeMethod(property, "signature").get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,11 +92,12 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setReadTimeout(10000);
|
||||
|
||||
connection.getResponseCode();
|
||||
try(InputStream is = connection.getInputStream(); Scanner scanner = new Scanner(is))
|
||||
{
|
||||
String response = scanner.useDelimiter("\\A").next();
|
||||
|
||||
JsonObject json = new JsonParser().parse(response).getAsJsonObject();
|
||||
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
|
||||
String name = json.get("name").getAsString();
|
||||
|
||||
JsonArray properties = json.getAsJsonArray("properties");
|
||||
@@ -121,32 +120,39 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
if(NpcApi.config.debug())
|
||||
e.printStackTrace();
|
||||
|
||||
skinCacheUUID.remove(uuid);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a {@link Skin} by player name.
|
||||
* Fetches a {@link Skin} by player name OR by a direct skin PNG URL.
|
||||
*
|
||||
* @param name the player username.
|
||||
* @param input the player username or a public URL pointing to a skin .png.
|
||||
* @return an {@link Optional} containing the skin if found, otherwise empty.
|
||||
*/
|
||||
public static Optional<Skin> fetchSkin(@NotNull String name)
|
||||
public static Optional<Skin> fetchSkin(@NotNull String input)
|
||||
{
|
||||
if(skinCacheName.containsKey(name))
|
||||
return Optional.ofNullable(skinCacheName.get(name));
|
||||
if (input.toLowerCase().startsWith("http://") || input.toLowerCase().startsWith("https://"))
|
||||
return fetchSkinByUrl(input);
|
||||
|
||||
if(skinCacheName.containsKey(input))
|
||||
return Optional.ofNullable(skinCacheName.get(input));
|
||||
|
||||
try
|
||||
{
|
||||
URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
|
||||
URL url = URI.create("https://api.mojang.com/users/profiles/minecraft/" + input).toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setReadTimeout(10000);
|
||||
|
||||
conn.getResponseCode();
|
||||
try(InputStream is = conn.getInputStream(); Scanner scanner = new Scanner(is))
|
||||
{
|
||||
String response = scanner.useDelimiter("\\A").next();
|
||||
JsonObject json = new JsonParser().parse(response).getAsJsonObject();
|
||||
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
|
||||
String id = json.get("id").getAsString();
|
||||
return fetchSkin(UUID.fromString(id.replaceFirst(
|
||||
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})",
|
||||
@@ -155,7 +161,56 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
skinCacheName.remove(name);
|
||||
if(NpcApi.config.debug())
|
||||
e.printStackTrace();
|
||||
|
||||
skinCacheName.remove(input);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate and fetch a skin via MineSkin using a public image URL.
|
||||
*/
|
||||
private static Optional<Skin> fetchSkinByUrl(@NotNull String urlString)
|
||||
{
|
||||
if(skinCacheUrl.containsKey(urlString))
|
||||
return Optional.ofNullable(skinCacheUrl.get(urlString));
|
||||
|
||||
try(HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build())
|
||||
{
|
||||
JsonObject jsonBody = new JsonObject();
|
||||
jsonBody.addProperty("url", urlString);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://api.mineskin.org/generate/url"))
|
||||
.timeout(Duration.ofSeconds(10))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("User-Agent", "MinecraftNpcApi")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody.toString()))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != 200)
|
||||
return Optional.empty();
|
||||
|
||||
JsonObject obj = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
JsonObject texture = obj.getAsJsonObject("data").getAsJsonObject("texture");
|
||||
|
||||
String value = texture.get("value").getAsString();
|
||||
String signature = texture.get("signature").getAsString();
|
||||
|
||||
Skin skin = new Skin(null, value, signature);
|
||||
skinCacheUrl.put(urlString, skin);
|
||||
return Optional.of(skin);
|
||||
}
|
||||
catch(IOException | InterruptedException e)
|
||||
{
|
||||
if(NpcApi.config.debug())
|
||||
e.printStackTrace();
|
||||
|
||||
skinCacheUrl.remove(urlString);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -175,9 +230,8 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
if(skinCacheFile.containsKey(skinFile))
|
||||
return Optional.ofNullable(skinCacheFile.get(skinFile));
|
||||
|
||||
try
|
||||
try(HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build())
|
||||
{
|
||||
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
|
||||
String boundary = "----MineSkinBoundary" + System.currentTimeMillis();
|
||||
String CRLF = "\r\n";
|
||||
|
||||
@@ -199,7 +253,7 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
JsonObject obj = new JsonParser().parse(response.body()).getAsJsonObject();
|
||||
JsonObject obj = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
JsonObject texture = obj.getAsJsonObject("data").getAsJsonObject("texture");
|
||||
|
||||
String value = texture.get("value").getAsString();
|
||||
@@ -249,14 +303,14 @@ public record Skin(@Nullable String name, @Nullable String value, @Nullable Stri
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fetches a skin by the player's name.
|
||||
* Asynchronously fetches a skin by the player's name or a public image URL.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @param nameOrUrl the name of the player or direct skin url string
|
||||
* @return a CompletableFuture containing an Optional of the Skin
|
||||
*/
|
||||
public static CompletableFuture<Optional<Skin>> fetchSkinAsync(@NotNull String name)
|
||||
public static CompletableFuture<Optional<Skin>> fetchSkinAsync(@NotNull String nameOrUrl)
|
||||
{
|
||||
CompletableFuture<Optional<Skin>> future = CompletableFuture.supplyAsync(() -> fetchSkin(name),
|
||||
CompletableFuture<Optional<Skin>> future = CompletableFuture.supplyAsync(() -> fetchSkin(nameOrUrl),
|
||||
runnable -> Bukkit.getScheduler().runTaskAsynchronously(NpcApi.plugin, runnable));
|
||||
Tasks.trackFuture(future);
|
||||
return future;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.eisi05.npc.api.pathfinding;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.Openable;
|
||||
@@ -11,8 +11,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class AStarPathfinder
|
||||
/**
|
||||
* Standard block-grid based A* pathfinder.
|
||||
*/
|
||||
public class AStarPathfinder extends AbstractPathfinder
|
||||
{
|
||||
private static final double[][][] MOVE_COSTS = new double[3][3][3];
|
||||
|
||||
@@ -33,48 +37,35 @@ public class AStarPathfinder
|
||||
}
|
||||
}
|
||||
|
||||
private final int maxIterations;
|
||||
private final boolean allowDiagonal;
|
||||
private final double entityHeight;
|
||||
private final double entityWidth;
|
||||
private final PriorityQueue<Node> openSet = new PriorityQueue<>();
|
||||
private final Set<Long> openSetIds = new HashSet<>();
|
||||
private final Map<Long, Node> allNodes = new HashMap<>();
|
||||
private final Long2ObjectOpenHashMap<Node> allNodes = new Long2ObjectOpenHashMap<>();
|
||||
private final Long2ObjectOpenHashMap<Collection<BoundingBox>> boundingBoxCache = new Long2ObjectOpenHashMap<>();
|
||||
private World world;
|
||||
|
||||
/**
|
||||
* Constructs a new AStarPathfinder instance with the specified configuration parameters.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed during pathfinding
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
*/
|
||||
public AStarPathfinder(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth)
|
||||
{
|
||||
this.maxIterations = maxIterations;
|
||||
this.allowDiagonal = allowDiagonal;
|
||||
this.entityHeight = entityHeight;
|
||||
this.entityWidth = entityWidth;
|
||||
super(maxIterations, allowDiagonal, entityHeight, entityWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block is valid to stand on.
|
||||
*/
|
||||
public static boolean isSafeFloor(Block block)
|
||||
{
|
||||
if(block == null)
|
||||
return false;
|
||||
|
||||
Material type = block.getType();
|
||||
if(type.isAir() || block.isLiquid())
|
||||
return false;
|
||||
|
||||
return !block.isPassable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a position is valid (not inside a solid block).
|
||||
* Checks if a target position is valid and clear of obstacles for an entity of given dimensions.
|
||||
*
|
||||
* @param world The world to check in
|
||||
* @param tx The x coordinate of the position
|
||||
* @param ty The y coordinate of the position
|
||||
* @param tz The z coordinate of the position
|
||||
* @param entityHeight The height of the entity
|
||||
* @param entityWidth The width of the entity
|
||||
* @return true if the position is valid, false otherwise
|
||||
* @param world the world in which to check the position
|
||||
* @param tx the target X coordinate (center)
|
||||
* @param ty the target Y coordinate (feet level)
|
||||
* @param tz the target Z coordinate (center)
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
* @return true if the position is valid and unoccupied, false otherwise
|
||||
*/
|
||||
public static boolean isPositionValid(@NotNull World world, double tx, double ty, double tz, double entityHeight, double entityWidth)
|
||||
{
|
||||
@@ -106,14 +97,25 @@ public class AStarPathfinder
|
||||
if(block.getBlockData() instanceof Openable)
|
||||
continue;
|
||||
|
||||
if(NpcApi.config.pathfindingPassableOverride().test(block))
|
||||
continue;
|
||||
|
||||
Collection<BoundingBox> blockBoxes = block.getCollisionShape().getBoundingBoxes();
|
||||
if(blockBoxes.isEmpty())
|
||||
continue;
|
||||
|
||||
for(BoundingBox blockBox : blockBoxes)
|
||||
{
|
||||
BoundingBox absoluteBox = blockBox.clone().shift(x, y, z);
|
||||
if(entityBox.overlaps(absoluteBox))
|
||||
double bMinX = blockBox.getMinX() + x;
|
||||
double bMaxX = blockBox.getMaxX() + x;
|
||||
double bMinY = blockBox.getMinY() + y;
|
||||
double bMaxY = blockBox.getMaxY() + y;
|
||||
double bMinZ = blockBox.getMinZ() + z;
|
||||
double bMaxZ = blockBox.getMaxZ() + z;
|
||||
|
||||
if(entityBox.getMinX() < bMaxX && entityBox.getMaxX() > bMinX &&
|
||||
entityBox.getMinY() < bMaxY && entityBox.getMaxY() > bMinY &&
|
||||
entityBox.getMinZ() < bMaxZ && entityBox.getMaxZ() > bMinZ)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +125,18 @@ public class AStarPathfinder
|
||||
return true;
|
||||
}
|
||||
|
||||
public @Nullable List<Location> getPath(@NotNull Location start, @NotNull Location end) throws PathfindingUtils.PathfindingException
|
||||
/**
|
||||
* Calculates a path from start to end locations using the A* algorithm, reporting progress updates via a listener.
|
||||
*
|
||||
* @param start the starting location
|
||||
* @param end the target location
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return a list of locations forming the path, or null if no path could be found
|
||||
* @throws PathfindingUtils.PathfindingException if the start or end location fails floor validity checks
|
||||
*/
|
||||
@Override
|
||||
public @Nullable List<Location> getPath(@NotNull Location start, @NotNull Location end, @Nullable BiConsumer<Double, Integer> progressListener)
|
||||
throws PathfindingUtils.PathfindingException
|
||||
{
|
||||
if(start.getWorld() == null || end.getWorld() == null)
|
||||
return null;
|
||||
@@ -151,6 +164,9 @@ public class AStarPathfinder
|
||||
startNode.gCost = 0;
|
||||
startNode.calculateH(end);
|
||||
|
||||
double startH = startNode.hCost;
|
||||
double minH = startH;
|
||||
|
||||
openSet.add(startNode);
|
||||
openSetIds.add(startNode.id);
|
||||
allNodes.put(startNode.id, startNode);
|
||||
@@ -159,7 +175,7 @@ public class AStarPathfinder
|
||||
|
||||
while(!openSet.isEmpty())
|
||||
{
|
||||
if(iterations > maxIterations)
|
||||
if(iterations >= maxIterations)
|
||||
return null;
|
||||
|
||||
iterations++;
|
||||
@@ -167,6 +183,12 @@ public class AStarPathfinder
|
||||
Node current = openSet.poll();
|
||||
openSetIds.remove(current.id);
|
||||
|
||||
if(startH > 0 && current.hCost < minH)
|
||||
minH = current.hCost;
|
||||
|
||||
if(progressListener != null)
|
||||
progressListener.accept(Math.clamp(1.0 - (minH / startH), 0.0, 1.0), iterations);
|
||||
|
||||
if(distanceSq(current, end) < 1.0)
|
||||
return retracePath(current);
|
||||
|
||||
@@ -228,6 +250,14 @@ public class AStarPathfinder
|
||||
* Advanced physics check. Checks whether we can move from one floor block to another.
|
||||
* <p>
|
||||
* The {@code fy} and {@code ty} values are floor-block Y coordinates. Entity feet and headspace are checked at {@code ty + 1} and {@code ty + 2}.
|
||||
*
|
||||
* @return true if movement between the blocks is valid, false otherwise
|
||||
* @code fx the source floor X coordinate
|
||||
* @code fy the source floor Y coordinate
|
||||
* @code fz the source floor Z coordinate
|
||||
* @code tx the target floor X coordinate
|
||||
* @code ty the target floor Y coordinate
|
||||
* @code tz the target floor Z coordinate
|
||||
*/
|
||||
private boolean canWalk(int fx, int fy, int fz, int tx, int ty, int tz)
|
||||
{
|
||||
@@ -288,17 +318,16 @@ public class AStarPathfinder
|
||||
for(int y = startY + 1; y >= startY - 6; y--)
|
||||
{
|
||||
Block block = w.getBlockAt(bx, y, bz);
|
||||
|
||||
if(block.getBlockData() instanceof Openable)
|
||||
continue;
|
||||
|
||||
if(block.isLiquid())
|
||||
continue;
|
||||
|
||||
if(!block.getType().isSolid() || block.isPassable())
|
||||
if(!block.getType().isSolid() || block.isPassable() || NpcApi.config.pathfindingPassableOverride().test(block))
|
||||
continue;
|
||||
|
||||
Collection<BoundingBox> boxes = block.getCollisionShape().getBoundingBoxes();
|
||||
Collection<BoundingBox> boxes = getCachedCollisions(block);
|
||||
if(boxes.isEmpty())
|
||||
return y;
|
||||
|
||||
@@ -314,6 +343,12 @@ public class AStarPathfinder
|
||||
return loc.getBlockY() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retraces the path backwards from the final node to the start node, building a list of locations.
|
||||
*
|
||||
* @param current the final target node
|
||||
* @return an ordered list of locations representing the path
|
||||
*/
|
||||
private @NotNull List<Location> retracePath(@NotNull Node current)
|
||||
{
|
||||
List<Location> path = new ArrayList<>();
|
||||
@@ -327,15 +362,31 @@ public class AStarPathfinder
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the exact Y coordinate for an entity's feet at a given block coordinate.
|
||||
*
|
||||
* @param x the block X coordinate
|
||||
* @param floorY the block Y coordinate of the floor
|
||||
* @param z the block Z coordinate
|
||||
* @return the precise vertical position for the entity's feet
|
||||
*/
|
||||
private double feetYAt(int x, int floorY, int z)
|
||||
{
|
||||
Block floor = world.getBlockAt(x, floorY, z);
|
||||
return floorY + topSurfaceAt(floor, 0.5, 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the top surface Y offset of a block's collision shape at a specific local coordinate.
|
||||
*
|
||||
* @param block the block to inspect
|
||||
* @param lx the local X coordinate within the block
|
||||
* @param lz the local Z coordinate within the block
|
||||
* @return the highest surface Y offset
|
||||
*/
|
||||
private double topSurfaceAt(@NotNull Block block, double lx, double lz)
|
||||
{
|
||||
Collection<BoundingBox> boxes = block.getCollisionShape().getBoundingBoxes();
|
||||
Collection<BoundingBox> boxes = getCachedCollisions(block);
|
||||
if(boxes.isEmpty())
|
||||
return 1.0;
|
||||
|
||||
@@ -359,6 +410,33 @@ public class AStarPathfinder
|
||||
return bestTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached block collision.
|
||||
*
|
||||
* @param block the block to retrieve cached collisions for
|
||||
* @return the corresponding collection of bounding boxes
|
||||
*/
|
||||
private @NotNull Collection<BoundingBox> getCachedCollisions(@NotNull Block block)
|
||||
{
|
||||
long key = packBlockCoord(block.getX(), block.getY(), block.getZ());
|
||||
Collection<BoundingBox> cached = boundingBoxCache.get(key);
|
||||
if(cached != null)
|
||||
return cached;
|
||||
|
||||
boolean bodyPassable = block.getBlockData() instanceof Openable || block.isEmpty() || block.isPassable() ||
|
||||
NpcApi.config.pathfindingPassableOverride().test(block);
|
||||
Collection<BoundingBox> boxes = bodyPassable ? Collections.emptyList() : block.getCollisionShape().getBoundingBoxes();
|
||||
boundingBoxCache.put(key, boxes);
|
||||
return boxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the squared distance between a path node and a target location.
|
||||
*
|
||||
* @return the squared distance
|
||||
* @code n the path node
|
||||
* @code l the target location
|
||||
*/
|
||||
private double distanceSq(@NotNull Node n, @NotNull Location l)
|
||||
{
|
||||
double dx = (n.x + 0.5) - l.getX();
|
||||
@@ -367,6 +445,9 @@ public class AStarPathfinder
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single node within the A* pathfinding grid.
|
||||
*/
|
||||
private static class Node implements Comparable<Node>
|
||||
{
|
||||
final int x, y, z;
|
||||
@@ -377,6 +458,14 @@ public class AStarPathfinder
|
||||
Node parent = null;
|
||||
boolean closed = false;
|
||||
|
||||
/**
|
||||
* Constructs a new Node with specified coordinates and an optional pre-computed identifier.
|
||||
*
|
||||
* @param x the block X coordinate
|
||||
* @param y the block Y coordinate
|
||||
* @param z the block Z coordinate
|
||||
* @param id the unique long identifier, or null to generate one
|
||||
*/
|
||||
public Node(int x, int y, int z, Long id)
|
||||
{
|
||||
this.x = x;
|
||||
@@ -385,11 +474,24 @@ public class AStarPathfinder
|
||||
this.id = (id != null) ? id : hash(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique long hash for the given grid coordinates.
|
||||
*
|
||||
* @param x the block X coordinate
|
||||
* @param y the block Y coordinate
|
||||
* @param z the block Z coordinate
|
||||
* @return a packed long hash representing the coordinates
|
||||
*/
|
||||
public static long hash(int x, int y, int z)
|
||||
{
|
||||
return ((long) x & 0x3FFFFFF) | (((long) z & 0x3FFFFFF) << 26) | (((long) y & 0xFFF) << 52);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the heuristic cost (H-cost) from this node to the end location.
|
||||
*
|
||||
* @param end the target destination location
|
||||
*/
|
||||
public void calculateH(@NotNull Location end)
|
||||
{
|
||||
double dx = (x + 0.5) - end.getX();
|
||||
@@ -398,15 +500,26 @@ public class AStarPathfinder
|
||||
this.hCost = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total estimated cost (F-cost) for this node, combining G-cost and H-cost.
|
||||
*
|
||||
* @return the total F-cost
|
||||
*/
|
||||
public double getFCost()
|
||||
{
|
||||
return gCost + hCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this node with another node based on their F-costs for priority queue ordering.
|
||||
*
|
||||
* @param other the other node to compare against
|
||||
* @return a negative integer, zero, or a positive integer as this node's F-cost is less than, equal to, or greater than the specified node's F-cost
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(@NotNull Node other)
|
||||
{
|
||||
return Double.compare(this.getFCost(), other.getFCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package de.eisi05.npc.api.pathfinding;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Base abstract class for NPC pathfinding algorithms. Handles shared configuration, safety checks, and surface resolution.
|
||||
*/
|
||||
public abstract class AbstractPathfinder
|
||||
{
|
||||
protected final int maxIterations;
|
||||
protected final boolean allowDiagonal;
|
||||
protected final double entityHeight;
|
||||
protected final double entityWidth;
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractPathfinder with the specified configuration.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed for pathfinding
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
*/
|
||||
public AbstractPathfinder(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth)
|
||||
{
|
||||
this.maxIterations = maxIterations;
|
||||
this.allowDiagonal = allowDiagonal;
|
||||
this.entityHeight = entityHeight;
|
||||
this.entityWidth = entityWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block is safe to stand on.
|
||||
*
|
||||
* @param block the block to check
|
||||
* @return true if the block is safe to stand on, false otherwise
|
||||
*/
|
||||
public static boolean isSafeFloor(@Nullable Block block)
|
||||
{
|
||||
if(block == null)
|
||||
return false;
|
||||
|
||||
Material type = block.getType();
|
||||
if(type.isAir() || block.isLiquid())
|
||||
return false;
|
||||
|
||||
return !block.isPassable() && !NpcApi.config.pathfindingPassableOverride().test(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs block coordinates into a single unique long identifier hash.
|
||||
*
|
||||
* @param x the block X coordinate
|
||||
* @param y the block Y coordinate
|
||||
* @param z the block Z coordinate
|
||||
* @return the packed coordinate hash
|
||||
*/
|
||||
protected static long packBlockCoord(int x, int y, int z)
|
||||
{
|
||||
return ((long) (x & 0x3FFFFFF)) | (((long) (z & 0x3FFFFFF)) << 26) | (((long) (y & 0xFFF)) << 52);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a path from start to end.
|
||||
*
|
||||
* @param start The starting location
|
||||
* @param end The target location
|
||||
* @return A list of locations forming the path, or null if unreachable
|
||||
* @throws PathfindingUtils.PathfindingException if start or end location is invalid
|
||||
*/
|
||||
public @Nullable List<Location> getPath(@NotNull Location start, @NotNull Location end) throws PathfindingUtils.PathfindingException
|
||||
{
|
||||
return getPath(start, end, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a path from start to end, reporting calculation progress.
|
||||
*
|
||||
* @param start The starting location
|
||||
* @param end The target location
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return A list of locations forming the path, or null if unreachable
|
||||
* @throws PathfindingUtils.PathfindingException if start or end location is invalid
|
||||
*/
|
||||
public abstract @Nullable List<Location> getPath(@NotNull Location start, @NotNull Location end, @Nullable BiConsumer<Double, Integer> progressListener)
|
||||
throws PathfindingUtils.PathfindingException;
|
||||
|
||||
/**
|
||||
* Functional interface representing a factory that creates Pathfinder instances. This avoids thread-safety issues by providing fresh instances per
|
||||
* calculation.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PathfinderFactory<T extends AbstractPathfinder>
|
||||
{
|
||||
PathfinderFactory<AStarPathfinder> ASTAR = AStarPathfinder::new;
|
||||
PathfinderFactory<BoundingBoxPathfinder> BOUNDING_BOX = BoundingBoxPathfinder::new;
|
||||
|
||||
/**
|
||||
* Creates a factory for the BoundingBoxPathfinder with a custom grid step.
|
||||
*
|
||||
* @param gridStep the grid step to use for the pathfinder
|
||||
* @return a new PathfinderFactory instance
|
||||
*/
|
||||
static PathfinderFactory<BoundingBoxPathfinder> boundingBox(double gridStep)
|
||||
{
|
||||
return (maxIterations, allowDiagonal, entityHeight, entityWidth) ->
|
||||
new BoundingBoxPathfinder(maxIterations, allowDiagonal, entityHeight, entityWidth, gridStep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the pathfinder with the specified parameters.
|
||||
* <p>
|
||||
* This factory method ensures thread safety by instantiating a fresh pathfinder for every calculation invocation, avoiding shared state issues across
|
||||
* threads.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed during path calculation
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity navigating the path
|
||||
* @param entityWidth the width of the entity navigating the path
|
||||
* @return a newly created, thread-safe instance of the pathfinder
|
||||
*/
|
||||
@NotNull T create(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,935 @@
|
||||
package de.eisi05.npc.api.pathfinding;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.Openable;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Continuous 3D Bounding-Box Theta* Pathfinder with Dynamic Obstacle Clearance Probing.
|
||||
* <p>
|
||||
* Operates completely independently of integer block centers. Uses entity-footprint scanning, sub-grid step offsets, 3D swept-volume collision detection, and
|
||||
* exact collision shape measurement. When obstacles like open trapdoors, fence edges, or wall frames partially block a path, the pathfinder calculates the
|
||||
* exact width/depth offset needed to clear the obstacle boundary and tests if the NPC can smoothly navigate around it.
|
||||
*/
|
||||
public class BoundingBoxPathfinder extends AbstractPathfinder
|
||||
{
|
||||
private final double gridStep;
|
||||
private final double maxJumpHeight;
|
||||
private final double maxFallDistance;
|
||||
private final double supportWidth;
|
||||
|
||||
private final PriorityQueue<SubNode> openSet = new PriorityQueue<>();
|
||||
private final Long2ObjectOpenHashMap<SubNode> allNodes = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
private final Long2ObjectOpenHashMap<BlockData> blockCache = new Long2ObjectOpenHashMap<>();
|
||||
private final Long2ObjectOpenHashMap<FootSupport> supportCache = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
private World world;
|
||||
|
||||
/**
|
||||
* Constructs a new BoundingBoxPathfinder with default step, jump height, and fall distance settings.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
*/
|
||||
public BoundingBoxPathfinder(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth)
|
||||
{
|
||||
this(maxIterations, allowDiagonal, entityHeight, entityWidth, 0.25, 1.25, 3.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new BoundingBoxPathfinder with a custom grid step and default movement limits.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
* @param gridStep the sub-grid step size
|
||||
*/
|
||||
public BoundingBoxPathfinder(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth, double gridStep)
|
||||
{
|
||||
this(maxIterations, allowDiagonal, entityHeight, entityWidth, gridStep, 1.25, 3.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new BoundingBoxPathfinder with fully customized parameters.
|
||||
*
|
||||
* @param maxIterations the maximum number of iterations allowed
|
||||
* @param allowDiagonal whether diagonal movement is permitted
|
||||
* @param entityHeight the height of the entity
|
||||
* @param entityWidth the width of the entity
|
||||
* @param gridStep the sub-grid step size
|
||||
* @param maxJumpHeight the maximum height the entity can jump
|
||||
* @param maxFallDistance the maximum safe fall distance for the entity
|
||||
*/
|
||||
public BoundingBoxPathfinder(int maxIterations, boolean allowDiagonal, double entityHeight, double entityWidth, double gridStep,
|
||||
double maxJumpHeight, double maxFallDistance)
|
||||
{
|
||||
super(maxIterations, allowDiagonal, entityHeight, entityWidth);
|
||||
this.gridStep = Math.clamp(0.01, gridStep, 1);
|
||||
this.maxJumpHeight = maxJumpHeight;
|
||||
this.maxFallDistance = maxFallDistance;
|
||||
this.supportWidth = Math.max(0.1, entityWidth * 0.85);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves ground support for an entity at the specified coordinates using default jump and fall constraints.
|
||||
*
|
||||
* @param world the world to check in
|
||||
* @param x the target X coordinate
|
||||
* @param currentY the current Y coordinate
|
||||
* @param z the target Z coordinate
|
||||
* @param entityWidth the width of the entity
|
||||
* @return a {@link FootSupport} instance describing the ground details
|
||||
*/
|
||||
public static @NotNull FootSupport resolveGroundSupport(@NotNull World world, double x, double currentY, double z, double entityWidth)
|
||||
{
|
||||
return resolveGroundSupport(world, x, currentY, z, entityWidth, 1.25, 3.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves ground support for an entity at the specified coordinates with custom movement limitations.
|
||||
*
|
||||
* @param world the world to check in
|
||||
* @param x the target X coordinate
|
||||
* @param currentY the current Y coordinate
|
||||
* @param z the target Z coordinate
|
||||
* @param entityWidth the width of the entity
|
||||
* @param maxJumpHeight the maximum jump height allowed
|
||||
* @param maxFallDistance the maximum fall distance allowed
|
||||
* @return a {@link FootSupport} instance describing the ground details
|
||||
*/
|
||||
public static @NotNull FootSupport resolveGroundSupport(@NotNull World world, double x, double currentY, double z, double entityWidth, double maxJumpHeight,
|
||||
double maxFallDistance)
|
||||
{
|
||||
double supportWidth = Math.max(0.1, entityWidth * 0.85);
|
||||
double radius = supportWidth / 2.0;
|
||||
double minX = x - radius;
|
||||
double maxX = x + radius;
|
||||
double minZ = z - radius;
|
||||
double maxZ = z + radius;
|
||||
|
||||
int minBlockX = (int) Math.floor(minX);
|
||||
int maxBlockX = (int) Math.floor(maxX);
|
||||
int minBlockZ = (int) Math.floor(minZ);
|
||||
int maxBlockZ = (int) Math.floor(maxZ);
|
||||
|
||||
int searchStartY = (int) Math.floor(currentY + maxJumpHeight);
|
||||
int searchEndY = (int) Math.floor(currentY - maxFallDistance);
|
||||
|
||||
double highestTopY = -Double.MAX_VALUE;
|
||||
boolean foundSolid = false;
|
||||
double hazardPenalty = 0.0;
|
||||
|
||||
for(int bx = minBlockX; bx <= maxBlockX; bx++)
|
||||
{
|
||||
for(int bz = minBlockZ; bz <= maxBlockZ; bz++)
|
||||
{
|
||||
for(int by = searchStartY; by >= searchEndY; by--)
|
||||
{
|
||||
Block block = world.getBlockAt(bx, by, bz);
|
||||
Material mat = block.getType();
|
||||
|
||||
if(block.getBlockData() instanceof Openable || block.isEmpty() || block.isPassable() || block.isLiquid() ||
|
||||
NpcApi.config.pathfindingPassableOverride().test(block))
|
||||
continue;
|
||||
|
||||
if(mat == Material.LAVA || mat == Material.FIRE || mat == Material.SOUL_FIRE || mat == Material.MAGMA_BLOCK)
|
||||
hazardPenalty += 10.0;
|
||||
|
||||
Collection<BoundingBox> boxes = block.getCollisionShape().getBoundingBoxes();
|
||||
if(boxes.isEmpty())
|
||||
{
|
||||
double top = by + 1.0;
|
||||
if(top <= currentY + maxJumpHeight && top > highestTopY)
|
||||
{
|
||||
highestTopY = top;
|
||||
foundSolid = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(BoundingBox bb : boxes)
|
||||
{
|
||||
double bMinX = bb.getMinX() + bx;
|
||||
double bMaxX = bb.getMaxX() + bx;
|
||||
double bMinZ = bb.getMinZ() + bz;
|
||||
double bMaxZ = bb.getMaxZ() + bz;
|
||||
|
||||
if(bMaxX >= minX && bMinX <= maxX && bMaxZ >= minZ && bMinZ <= maxZ)
|
||||
{
|
||||
double top = bb.getMaxY() + by;
|
||||
if(top <= currentY + maxJumpHeight && top > highestTopY)
|
||||
{
|
||||
highestTopY = top;
|
||||
foundSolid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundSolid ? new FootSupport(highestTopY, hazardPenalty) : FootSupport.INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sub-grid step size used by the pathfinder.
|
||||
*
|
||||
* @return the sub-grid step size
|
||||
*/
|
||||
public double getGridStep()
|
||||
{
|
||||
return gridStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached block collision and hazard metadata for a specific block coordinate.
|
||||
*
|
||||
* @param bx the block X coordinate
|
||||
* @param by the block Y coordinate
|
||||
* @param bz the block Z coordinate
|
||||
* @return the corresponding {@link BlockData}
|
||||
*/
|
||||
private BlockData getCachedBlock(int bx, int by, int bz)
|
||||
{
|
||||
long key = packBlockCoord(bx, by, bz);
|
||||
BlockData cached = blockCache.get(key);
|
||||
if(cached != null)
|
||||
return cached;
|
||||
|
||||
Block block = world.getBlockAt(bx, by, bz);
|
||||
Material mat = block.getType();
|
||||
double hazard = (mat == Material.LAVA || mat == Material.FIRE || mat == Material.SOUL_FIRE || mat == Material.MAGMA_BLOCK) ? 10.0 : 0.0;
|
||||
|
||||
boolean bodyPassable = block.getBlockData() instanceof Openable || block.isEmpty() || block.isPassable() ||
|
||||
NpcApi.config.pathfindingPassableOverride().test(block);
|
||||
Collection<BoundingBox> boxes = bodyPassable ? Collections.emptyList() : block.getCollisionShape().getBoundingBoxes();
|
||||
|
||||
boolean isFootingSolid = !bodyPassable && !block.isLiquid();
|
||||
|
||||
BlockData data = new BlockData(boxes, isFootingSolid, hazard);
|
||||
blockCache.put(key, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity's bounding box is entirely valid and free of obstacles at the given position using cached blocks.
|
||||
*
|
||||
* @param x the target center X coordinate
|
||||
* @param y the target feet Y coordinate
|
||||
* @param z the target center Z coordinate
|
||||
* @return true if the box position is valid, false otherwise
|
||||
*/
|
||||
private boolean isBoxValidAtCached(double x, double y, double z)
|
||||
{
|
||||
double radius = entityWidth / 2.0;
|
||||
BoundingBox entityBox = new BoundingBox(
|
||||
x - radius + 0.001, y + 0.001, z - radius + 0.001,
|
||||
x + radius - 0.001, y + entityHeight - 0.001, z + radius - 0.001
|
||||
);
|
||||
|
||||
int minBlockX = (int) Math.floor(entityBox.getMinX());
|
||||
int maxBlockX = (int) Math.floor(entityBox.getMaxX());
|
||||
int minBlockY = (int) Math.floor(entityBox.getMinY());
|
||||
int maxBlockY = (int) Math.floor(entityBox.getMaxY());
|
||||
int minBlockZ = (int) Math.floor(entityBox.getMinZ());
|
||||
int maxBlockZ = (int) Math.floor(entityBox.getMaxZ());
|
||||
|
||||
for(int bx = minBlockX; bx <= maxBlockX; bx++)
|
||||
{
|
||||
for(int by = minBlockY; by <= maxBlockY; by++)
|
||||
{
|
||||
for(int bz = minBlockZ; bz <= maxBlockZ; bz++)
|
||||
{
|
||||
for(BoundingBox blockBox : getCachedBlock(bx, by, bz).collisionBoxes())
|
||||
{
|
||||
double bMinX = blockBox.getMinX() + bx;
|
||||
double bMaxX = blockBox.getMaxX() + bx;
|
||||
double bMinY = blockBox.getMinY() + by;
|
||||
double bMaxY = blockBox.getMaxY() + by;
|
||||
double bMinZ = blockBox.getMinZ() + bz;
|
||||
double bMaxZ = blockBox.getMaxZ() + bz;
|
||||
|
||||
if(entityBox.getMinX() < bMaxX && entityBox.getMaxX() > bMinX &&
|
||||
entityBox.getMinY() < bMaxY && entityBox.getMaxY() > bMinY &&
|
||||
entityBox.getMinZ() < bMaxZ && entityBox.getMaxZ() > bMinZ)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a swept-volume collision check along a movement path from one point to another.
|
||||
*
|
||||
* @param x1 start X coordinate
|
||||
* @param y1 start Y coordinate
|
||||
* @param z1 start Z coordinate
|
||||
* @param x2 target X coordinate
|
||||
* @param y2 target Y coordinate
|
||||
* @param z2 target Z coordinate
|
||||
* @param checkGround whether to validate ground support along the sweep
|
||||
* @return true if the movement path is clear, false otherwise
|
||||
*/
|
||||
private boolean canSweep(double x1, double y1, double z1, double x2, double y2, double z2, boolean checkGround)
|
||||
{
|
||||
double dx = x2 - x1;
|
||||
double dy = y2 - y1;
|
||||
double dz = z2 - z1;
|
||||
double hDist = Math.sqrt(dx * dx + dz * dz);
|
||||
|
||||
if(hDist < 0.0001 && Math.abs(dy) < 0.0001)
|
||||
return true;
|
||||
|
||||
double safeStep = Math.max(0.1, entityWidth / 2.0);
|
||||
|
||||
if(dy > 0.0001)
|
||||
{
|
||||
int vSteps = Math.max(1, (int) Math.ceil(dy / safeStep));
|
||||
double vStepY = dy / vSteps;
|
||||
for(int i = 0; i <= vSteps; i++)
|
||||
{
|
||||
if(!isBoxValidAtCached(x1, y1 + (vStepY * i), z1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double sweepY = Math.max(y1, y2);
|
||||
if(hDist > 0.0001)
|
||||
{
|
||||
int hSteps = Math.max(1, (int) Math.ceil(hDist / safeStep));
|
||||
double stepX = dx / hSteps;
|
||||
double stepZ = dz / hSteps;
|
||||
double lastFeetY = y1;
|
||||
|
||||
for(int i = 0; i <= hSteps; i++)
|
||||
{
|
||||
double cx = x1 + (stepX * i);
|
||||
double cz = z1 + (stepZ * i);
|
||||
|
||||
if(!isBoxValidAtCached(cx, sweepY, cz))
|
||||
return false;
|
||||
|
||||
if(checkGround)
|
||||
{
|
||||
FootSupport support = resolveFootSupport(cx, sweepY, cz);
|
||||
if(!support.valid())
|
||||
return false;
|
||||
|
||||
if(i > 0)
|
||||
{
|
||||
double stepYDiff = support.feetY() - lastFeetY;
|
||||
if(stepYDiff > maxJumpHeight || stepYDiff < -maxFallDistance)
|
||||
return false;
|
||||
}
|
||||
lastFeetY = support.feetY();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dy < -0.0001)
|
||||
{
|
||||
int vSteps = Math.max(1, (int) Math.ceil(Math.abs(dy) / safeStep));
|
||||
double vStepY = Math.abs(dy) / vSteps;
|
||||
for(int i = 0; i <= vSteps; i++)
|
||||
{
|
||||
if(!isBoxValidAtCached(x2, y1 - (vStepY * i), z2))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a general movement sweep is valid between two points without ground validation.
|
||||
*
|
||||
* @param x1 start X
|
||||
* @param y1 start Y
|
||||
* @param z1 start Z
|
||||
* @param x2 target X
|
||||
* @param y2 target Y
|
||||
* @param z2 target Z
|
||||
* @return true if the movement is valid
|
||||
*/
|
||||
public boolean canSweepMove(double x1, double y1, double z1, double x2, double y2, double z2)
|
||||
{
|
||||
return canSweep(x1, y1, z1, x2, y2, z2, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a walking sweep movement is valid between two points, including ground support verification.
|
||||
*
|
||||
* @param x1 start X
|
||||
* @param y1 start Y
|
||||
* @param z1 start Z
|
||||
* @param x2 target X
|
||||
* @param y2 target Y
|
||||
* @param z2 target Z
|
||||
* @return true if the walking move is valid
|
||||
*/
|
||||
private boolean canSweepWalk(double x1, double y1, double z1, double x2, double y2, double z2)
|
||||
{
|
||||
return canSweep(x1, y1, z1, x2, y2, z2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes and processes obstacle clearance offsets around a target point, evaluating alternative candidate points.
|
||||
*
|
||||
* @param targetX target X coordinate
|
||||
* @param targetY target Y coordinate
|
||||
* @param targetZ target Z coordinate
|
||||
* @param current current sub-node being expanded
|
||||
* @param end the ultimate destination location
|
||||
*/
|
||||
private void processClearanceOffsets(double targetX, double targetY, double targetZ, SubNode current, Location end)
|
||||
{
|
||||
double radius = entityWidth / 2.0;
|
||||
double margin = 0.01;
|
||||
|
||||
double eMinX = targetX - radius;
|
||||
double eMaxX = targetX + radius;
|
||||
double eMinY = targetY + 0.001;
|
||||
double eMaxY = targetY + entityHeight - 0.001;
|
||||
double eMinZ = targetZ - radius;
|
||||
double eMaxZ = targetZ + radius;
|
||||
|
||||
int minBlockX = (int) Math.floor(eMinX);
|
||||
int maxBlockX = (int) Math.floor(eMaxX);
|
||||
int minBlockY = (int) Math.floor(eMinY);
|
||||
int maxBlockY = (int) Math.floor(eMaxY);
|
||||
int minBlockZ = (int) Math.floor(eMinZ);
|
||||
int maxBlockZ = (int) Math.floor(eMaxZ);
|
||||
|
||||
for(int bx = minBlockX; bx <= maxBlockX; bx++)
|
||||
{
|
||||
for(int by = minBlockY; by <= maxBlockY; by++)
|
||||
{
|
||||
for(int bz = minBlockZ; bz <= maxBlockZ; bz++)
|
||||
{
|
||||
for(BoundingBox bb : getCachedBlock(bx, by, bz).collisionBoxes())
|
||||
{
|
||||
double bMinX = bb.getMinX() + bx;
|
||||
double bMaxX = bb.getMaxX() + bx;
|
||||
double bMinY = bb.getMinY() + by;
|
||||
double bMaxY = bb.getMaxY() + by;
|
||||
double bMinZ = bb.getMinZ() + bz;
|
||||
double bMaxZ = bb.getMaxZ() + bz;
|
||||
|
||||
if(eMinX < bMaxX && eMaxX > bMinX && eMinY < bMaxY && eMaxY > bMinY && eMinZ < bMaxZ && eMaxZ > bMinZ)
|
||||
{
|
||||
double shiftPlusX = bMaxX + radius + margin;
|
||||
double shiftMinusX = bMinX - radius - margin;
|
||||
double shiftPlusZ = bMaxZ + radius + margin;
|
||||
double shiftMinusZ = bMinZ - radius - margin;
|
||||
|
||||
evaluateCandidatePoint(shiftPlusX, targetZ, current, end);
|
||||
evaluateCandidatePoint(shiftMinusX, targetZ, current, end);
|
||||
evaluateCandidatePoint(targetX, shiftPlusZ, current, end);
|
||||
evaluateCandidatePoint(targetX, shiftMinusZ, current, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves foot support using cached block data and support cache lookups.
|
||||
*
|
||||
* @param x the target X coordinate
|
||||
* @param currentY the current Y coordinate
|
||||
* @param z the target Z coordinate
|
||||
* @return the resolved {@link FootSupport}
|
||||
*/
|
||||
private @NotNull FootSupport resolveFootSupport(double x, double currentY, double z)
|
||||
{
|
||||
long cacheKey = SubNode.hash(x, currentY, z, gridStep / 2.0);
|
||||
FootSupport cached = supportCache.get(cacheKey);
|
||||
if(cached != null)
|
||||
return cached;
|
||||
|
||||
double radius = supportWidth / 2.0;
|
||||
double minX = x - radius;
|
||||
double maxX = x + radius;
|
||||
double minZ = z - radius;
|
||||
double maxZ = z + radius;
|
||||
|
||||
int minBlockX = (int) Math.floor(minX);
|
||||
int maxBlockX = (int) Math.floor(maxX);
|
||||
int minBlockZ = (int) Math.floor(minZ);
|
||||
int maxBlockZ = (int) Math.floor(maxZ);
|
||||
|
||||
int searchStartY = (int) Math.floor(currentY + maxJumpHeight);
|
||||
int searchEndY = (int) Math.floor(currentY - maxFallDistance);
|
||||
|
||||
double highestTopY = -Double.MAX_VALUE;
|
||||
boolean foundSolid = false;
|
||||
double hazardPenalty = 0.0;
|
||||
|
||||
for(int bx = minBlockX; bx <= maxBlockX; bx++)
|
||||
{
|
||||
for(int bz = minBlockZ; bz <= maxBlockZ; bz++)
|
||||
{
|
||||
for(int by = searchStartY; by >= searchEndY; by--)
|
||||
{
|
||||
BlockData blockData = getCachedBlock(bx, by, bz);
|
||||
hazardPenalty += blockData.hazardPenalty();
|
||||
|
||||
if(!blockData.isFootingSolid())
|
||||
continue;
|
||||
|
||||
Collection<BoundingBox> boxes = blockData.collisionBoxes();
|
||||
if(boxes.isEmpty())
|
||||
{
|
||||
double top = by + 1.0;
|
||||
if(top <= currentY + maxJumpHeight && top > highestTopY)
|
||||
{
|
||||
highestTopY = top;
|
||||
foundSolid = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(BoundingBox bb : boxes)
|
||||
{
|
||||
double bMinX = bb.getMinX() + bx;
|
||||
double bMaxX = bb.getMaxX() + bx;
|
||||
double bMinZ = bb.getMinZ() + bz;
|
||||
double bMaxZ = bb.getMaxZ() + bz;
|
||||
|
||||
if(bMaxX >= minX && bMinX <= maxX && bMaxZ >= minZ && bMinZ <= maxZ)
|
||||
{
|
||||
double top = bb.getMaxY() + by;
|
||||
if(top <= currentY + maxJumpHeight && top > highestTopY)
|
||||
{
|
||||
highestTopY = top;
|
||||
foundSolid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FootSupport result = foundSolid ? new FootSupport(highestTopY, hazardPenalty) : FootSupport.INVALID;
|
||||
supportCache.put(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an optimized continuous 3D path from start to end using the Bounding-Box Theta* algorithm.
|
||||
*
|
||||
* @param start the starting location
|
||||
* @param end the target destination location
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return a list of locations representing the smoothed path, or null if unreachable
|
||||
* @throws PathfindingUtils.PathfindingException if start or end locations lack valid floor support
|
||||
*/
|
||||
@Override
|
||||
public @Nullable List<Location> getPath(@NotNull Location start, @NotNull Location end, @Nullable BiConsumer<Double, Integer> progressListener)
|
||||
throws PathfindingUtils.PathfindingException
|
||||
{
|
||||
if(start.getWorld() == null || end.getWorld() == null || !start.getWorld().equals(end.getWorld()))
|
||||
return null;
|
||||
|
||||
this.world = start.getWorld();
|
||||
openSet.clear();
|
||||
allNodes.clear();
|
||||
blockCache.clear();
|
||||
supportCache.clear();
|
||||
|
||||
FootSupport startSupport = resolveFootSupport(start.getX(), start.getY(), start.getZ());
|
||||
FootSupport endSupport = resolveFootSupport(end.getX(), end.getY(), end.getZ());
|
||||
|
||||
double startFeetY = startSupport.valid ? startSupport.feetY : start.getY();
|
||||
double endFeetY = endSupport.valid ? endSupport.feetY : end.getY();
|
||||
|
||||
if(NpcApi.config.checkValidPath())
|
||||
{
|
||||
if(!startSupport.valid)
|
||||
throw new PathfindingUtils.PathfindingException("Start location has no valid floor support: " + start);
|
||||
if(!endSupport.valid)
|
||||
throw new PathfindingUtils.PathfindingException("End location has no valid floor support: " + end);
|
||||
}
|
||||
|
||||
SubNode startNode = createSubNode(start.getX(), startFeetY, start.getZ(), null);
|
||||
startNode.gCost = 0;
|
||||
startNode.calculateH(end);
|
||||
|
||||
double startH = startNode.hCost;
|
||||
|
||||
openSet.add(startNode);
|
||||
allNodes.put(startNode.id, startNode);
|
||||
|
||||
SubNode bestNode = startNode;
|
||||
double bestHCost = startNode.hCost;
|
||||
|
||||
int iterations = 0;
|
||||
double[][] directions;
|
||||
if(allowDiagonal)
|
||||
{
|
||||
directions = new double[][]{
|
||||
{1, 0}, {0, 1}, {-1, 0}, {0, -1},
|
||||
{0.7071, 0.7071}, {-0.7071, 0.7071}, {0.7071, -0.7071}, {-0.7071, -0.7071},
|
||||
{0.9238, 0.3826}, {0.3826, 0.9238}, {-0.3826, 0.9238}, {-0.9238, 0.3826},
|
||||
{-0.9238, -0.3826}, {-0.3826, -0.9238}, {0.3826, -0.9238}, {0.9238, -0.3826}
|
||||
};
|
||||
}
|
||||
else
|
||||
directions = new double[][]{{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
|
||||
|
||||
while(!openSet.isEmpty())
|
||||
{
|
||||
if(iterations >= maxIterations)
|
||||
return null;
|
||||
|
||||
iterations++;
|
||||
|
||||
SubNode current = openSet.poll();
|
||||
if(current.closed)
|
||||
continue;
|
||||
|
||||
current.closed = true;
|
||||
if(startH > 0 && current.hCost < bestHCost)
|
||||
{
|
||||
bestHCost = current.hCost;
|
||||
bestNode = current;
|
||||
}
|
||||
|
||||
if(progressListener != null)
|
||||
progressListener.accept(Math.clamp(1.0 - (bestHCost / startH), 0.0, 1.0), iterations);
|
||||
|
||||
if(current.distanceSqTo(end) < (gridStep * gridStep * 1.5))
|
||||
{
|
||||
List<Location> rawPath = retracePath(current);
|
||||
return simplifyPath(rawPath, end);
|
||||
}
|
||||
|
||||
double endYDiff = endFeetY - current.y;
|
||||
if(endYDiff <= maxJumpHeight && endYDiff >= -maxFallDistance && canSweepWalk(current.x, current.y, current.z, end.getX(), endFeetY, end.getZ()))
|
||||
{
|
||||
SubNode endNode = createSubNode(end.getX(), endFeetY, end.getZ(), null);
|
||||
double directCost = current.gCost + current.distanceTo(end.getX(), endFeetY, end.getZ());
|
||||
if(directCost < endNode.gCost)
|
||||
{
|
||||
endNode.gCost = directCost;
|
||||
endNode.parent = current;
|
||||
}
|
||||
List<Location> rawPath = retracePath(endNode);
|
||||
return simplifyPath(rawPath, end);
|
||||
}
|
||||
|
||||
for(double[] dir : directions)
|
||||
{
|
||||
double targetX = current.x + (dir[0] * gridStep);
|
||||
double targetZ = current.z + (dir[1] * gridStep);
|
||||
|
||||
evaluateCandidatePoint(targetX, targetZ, current, end);
|
||||
if(!isBoxValidAtCached(targetX, current.y, targetZ))
|
||||
processClearanceOffsets(targetX, current.y, targetZ, current, end);
|
||||
}
|
||||
}
|
||||
|
||||
if(bestNode == startNode)
|
||||
return null;
|
||||
|
||||
List<Location> rawPath = retracePath(bestNode);
|
||||
return simplifyPath(rawPath, rawPath.isEmpty() ? end : rawPath.getLast());
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a candidate point during path exploration, updating costs and parent nodes if optimal.
|
||||
*
|
||||
* @param nextX the target candidate X coordinate
|
||||
* @param nextZ the target candidate Z coordinate
|
||||
* @param current the current sub-node
|
||||
* @param end the target end location
|
||||
*/
|
||||
private void evaluateCandidatePoint(double nextX, double nextZ, SubNode current, Location end)
|
||||
{
|
||||
FootSupport footSupport = resolveFootSupport(nextX, current.y, nextZ);
|
||||
if(!footSupport.valid())
|
||||
return;
|
||||
|
||||
double nextFeetY = footSupport.feetY();
|
||||
double yDiff = nextFeetY - current.y;
|
||||
|
||||
if(yDiff > maxJumpHeight || yDiff < -maxFallDistance)
|
||||
return;
|
||||
|
||||
if(!canSweepMove(current.x, current.y, current.z, nextX, nextFeetY, nextZ))
|
||||
return;
|
||||
|
||||
SubNode neighbor = createSubNode(nextX, nextFeetY, nextZ, null);
|
||||
if(neighbor.closed)
|
||||
return;
|
||||
|
||||
SubNode parentCandidate = (current.parent != null) ? current.parent : current;
|
||||
boolean losFromParent = false;
|
||||
|
||||
if(parentCandidate != current)
|
||||
{
|
||||
double pYDiff = nextFeetY - parentCandidate.y;
|
||||
if(Math.abs(pYDiff) <= 0.1)
|
||||
losFromParent = canSweepWalk(parentCandidate.x, parentCandidate.y, parentCandidate.z, nextX, nextFeetY, nextZ);
|
||||
}
|
||||
|
||||
SubNode selectedParent = losFromParent ? parentCandidate : current;
|
||||
double stepDistance = selectedParent.distanceTo(nextX, nextFeetY, nextZ);
|
||||
double newGCost = selectedParent.gCost + stepDistance + footSupport.hazardPenalty();
|
||||
|
||||
if(newGCost < neighbor.gCost)
|
||||
{
|
||||
neighbor.gCost = newGCost;
|
||||
neighbor.calculateH(end);
|
||||
neighbor.parent = selectedParent;
|
||||
openSet.add(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or retrieves a sub-node for the given coordinates from the node registry.
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
* @param explicitId explicit node identifier, or null to auto-generate
|
||||
* @return the created or cached sub-node
|
||||
*/
|
||||
private SubNode createSubNode(double x, double y, double z, @Nullable Long explicitId)
|
||||
{
|
||||
long id = (explicitId != null) ? explicitId : SubNode.hash(x, y, z, gridStep);
|
||||
SubNode existing = allNodes.get(id);
|
||||
if(existing != null)
|
||||
return existing;
|
||||
|
||||
SubNode node = new SubNode(x, y, z, id);
|
||||
allNodes.put(id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retraces the path backwards from the given sub-node to construct a raw location list.
|
||||
*
|
||||
* @param current the target sub-node
|
||||
* @return a list of locations representing the raw path
|
||||
*/
|
||||
private @NotNull List<Location> retracePath(@NotNull SubNode current)
|
||||
{
|
||||
List<Location> path = new ArrayList<>();
|
||||
while(current != null)
|
||||
{
|
||||
path.add(new Location(world, current.x, current.y, current.z));
|
||||
current = current.parent;
|
||||
}
|
||||
Collections.reverse(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplifies and smooths a raw path using line-of-sight sweep checks.
|
||||
*
|
||||
* @param path the raw path locations
|
||||
* @param targetEnd the final target destination
|
||||
* @return a smoothed list of path locations
|
||||
*/
|
||||
private List<Location> simplifyPath(List<Location> path, Location targetEnd)
|
||||
{
|
||||
if(path.isEmpty())
|
||||
return path;
|
||||
|
||||
List<Location> smoothPath = new ArrayList<>();
|
||||
Location current = path.getFirst();
|
||||
smoothPath.add(current);
|
||||
|
||||
int i = 0;
|
||||
while(i < path.size() - 1)
|
||||
{
|
||||
int furthestVisible = i + 1;
|
||||
for(int j = i + 2; j < path.size(); j++)
|
||||
{
|
||||
Location target = path.get(j);
|
||||
if(Math.abs(target.getY() - current.getY()) > 0.1)
|
||||
break;
|
||||
|
||||
if(canSweepWalk(current.getX(), current.getY(), current.getZ(), target.getX(), target.getY(), target.getZ()))
|
||||
furthestVisible = j;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
current = path.get(furthestVisible);
|
||||
smoothPath.add(current);
|
||||
i = furthestVisible;
|
||||
}
|
||||
|
||||
Location lastPoint = smoothPath.getLast();
|
||||
|
||||
if(Math.abs(targetEnd.getY() - lastPoint.getY()) <= 0.1)
|
||||
{
|
||||
if(canSweepWalk(lastPoint.getX(), lastPoint.getY(), lastPoint.getZ(), targetEnd.getX(), targetEnd.getY(), targetEnd.getZ()))
|
||||
smoothPath.add(targetEnd.clone());
|
||||
}
|
||||
else
|
||||
smoothPath.add(targetEnd.clone());
|
||||
|
||||
return smoothPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record holding cached collision metadata, footing solidity, and hazard penalties for a block.
|
||||
*/
|
||||
private record BlockData(Collection<BoundingBox> collisionBoxes, boolean isFootingSolid, double hazardPenalty) {}
|
||||
|
||||
/**
|
||||
* Record representing ground support metadata beneath an entity's feet.
|
||||
*/
|
||||
public record FootSupport(double feetY, boolean valid, double hazardPenalty)
|
||||
{
|
||||
/**
|
||||
* Constant representing an invalid ground support state.
|
||||
*/
|
||||
public static final FootSupport INVALID = new FootSupport(0, false, 0);
|
||||
|
||||
/**
|
||||
* Constructs a valid FootSupport instance with the given feet Y coordinate and hazard penalty.
|
||||
*
|
||||
* @param feetY the vertical feet position
|
||||
* @param hazardPenalty the hazard penalty value
|
||||
*/
|
||||
public FootSupport(double feetY, double hazardPenalty)
|
||||
{
|
||||
this(feetY, true, hazardPenalty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a continuous 3D coordinate node within the BoundingBox pathfinding grid.
|
||||
*/
|
||||
private static class SubNode implements Comparable<SubNode>
|
||||
{
|
||||
final double x, y, z;
|
||||
final long id;
|
||||
|
||||
double gCost = Double.MAX_VALUE;
|
||||
double hCost = 0;
|
||||
SubNode parent = null;
|
||||
boolean closed = false;
|
||||
|
||||
/**
|
||||
* Constructs a new SubNode.
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
* @param id unique long identifier
|
||||
*/
|
||||
public SubNode(double x, double y, double z, long id)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a packed long hash for continuous 3D coordinates based on a grid step size.
|
||||
*
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
* @param gridStep the grid step size
|
||||
* @return the packed coordinate hash
|
||||
*/
|
||||
public static long hash(double x, double y, double z, double gridStep)
|
||||
{
|
||||
long gx = Math.round(x / gridStep);
|
||||
long gy = Math.round(y / gridStep);
|
||||
long gz = Math.round(z / gridStep);
|
||||
return (gx & 0x1FFFFFFL) | ((gz & 0x1FFFFFFL) << 25) | ((gy & 0x3FFFL) << 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the heuristic cost (H-cost) to the destination location.
|
||||
*
|
||||
* @param end the target destination location
|
||||
*/
|
||||
public void calculateH(@NotNull Location end)
|
||||
{
|
||||
double dx = x - end.getX();
|
||||
double dy = y - end.getY();
|
||||
double dz = z - end.getZ();
|
||||
this.hCost = Math.sqrt(dx * dx + dy * dy + dz * dz) * 1.001;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Euclidean distance to specific target coordinates.
|
||||
*
|
||||
* @param targetX target X
|
||||
* @param targetY target Y
|
||||
* @param targetZ target Z
|
||||
* @return the distance
|
||||
*/
|
||||
public double distanceTo(double targetX, double targetY, double targetZ)
|
||||
{
|
||||
double dx = x - targetX;
|
||||
double dy = y - targetY;
|
||||
double dz = z - targetZ;
|
||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the squared distance to a target location.
|
||||
*
|
||||
* @param l the target location
|
||||
* @return the squared distance
|
||||
*/
|
||||
public double distanceSqTo(@NotNull Location l)
|
||||
{
|
||||
double dx = x - l.getX();
|
||||
double dy = y - l.getY();
|
||||
double dz = z - l.getZ();
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total estimated F-cost (G-cost + H-cost).
|
||||
*
|
||||
* @return the F-cost
|
||||
*/
|
||||
public double getFCost()
|
||||
{
|
||||
return gCost + hCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this node with another sub-node based on their F-costs.
|
||||
*
|
||||
* @param other the other sub-node
|
||||
* @return comparison result (-1, 0, or 1)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(@NotNull SubNode other)
|
||||
{
|
||||
return Double.compare(this.getFCost(), other.getFCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,16 @@ public class Path implements ConfigurationSerializable
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new path that is the reverse of this path.
|
||||
*
|
||||
* @return a new path that is the reverse of this path
|
||||
*/
|
||||
public @NotNull Path reverse()
|
||||
{
|
||||
return new Path(new ArrayList<>(locations).reversed(), new ArrayList<>(waypoints).reversed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path as an unmodifiable list of {@link Vector} objects.
|
||||
*
|
||||
@@ -95,8 +105,8 @@ public class Path implements ConfigurationSerializable
|
||||
if(locations.isEmpty())
|
||||
return "Empty path";
|
||||
|
||||
Vector start = vectors.get(0);
|
||||
Vector end = vectors.get(vectors.size() - 1);
|
||||
Vector start = vectors.getFirst();
|
||||
Vector end = vectors.getLast();
|
||||
|
||||
return String.format("Start: [%.2f, %.2f, %.2f] -> End: [%.2f, %.2f, %.2f]",
|
||||
start.getX(), start.getY(), start.getZ(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.eisi05.npc.api.pathfinding;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import de.eisi05.npc.api.objects.NpcConfig;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -9,6 +10,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
@@ -17,125 +19,121 @@ import java.util.function.BiConsumer;
|
||||
public class PathfindingUtils
|
||||
{
|
||||
/**
|
||||
* Asynchronously calculates a path through a list of waypoints.
|
||||
* <p>
|
||||
* Each segment between consecutive waypoints is calculated in parallel using {@link CompletableFuture}. The returned future completes with a {@link Path}
|
||||
* containing the full path, or completes exceptionally if an {@link PathfindingException} occurs.
|
||||
* Asynchronously calculates a path through a list of waypoints using the default {@link NpcConfig#pathfinderFactory()}.
|
||||
*
|
||||
* @param waypoints the ordered list of locations to traverse
|
||||
* @param maxIterations the maximum number of iterations the A* algorithm will attempt per segment
|
||||
* @param allowDiagonalMovement whether diagonal movement is allowed
|
||||
* @param progressListener a progress listener with the signature (segmentIndex, totalSegments)
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return a {@link CompletableFuture} that completes with the calculated {@link Path}
|
||||
*/
|
||||
public static @NotNull CompletableFuture<Path> findPathAsync(@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
@Nullable BiConsumer<Integer, Integer> progressListener)
|
||||
@Nullable BiConsumer<Double, Integer> progressListener)
|
||||
{
|
||||
return findPathAsync(null, waypoints, maxIterations, allowDiagonalMovement, 1.8, 0.6, progressListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously calculates a path through a list of waypoints using a specified pathfinder factory.
|
||||
*
|
||||
* @param factory the factory to create the pathfinder, if null the default factory {@link NpcConfig#pathfinderFactory()} will be used
|
||||
* @param waypoints the ordered list of locations to traverse
|
||||
* @param maxIterations the maximum number of iterations the algorithm will attempt per segment
|
||||
* @param allowDiagonalMovement whether diagonal movement is allowed
|
||||
* @param entityHeight the height of the entity traversing the path
|
||||
* @param entityWidth the width of the entity traversing the path
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return a {@link CompletableFuture} that completes with the calculated {@link Path}
|
||||
*/
|
||||
public static @NotNull CompletableFuture<Path> findPathAsync(@Nullable AbstractPathfinder.PathfinderFactory factory, @NotNull List<Location> waypoints,
|
||||
int maxIterations, boolean allowDiagonalMovement, double entityHeight, double entityWidth,
|
||||
@Nullable BiConsumer<Double, Integer> progressListener)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return findPath(waypoints, maxIterations, allowDiagonalMovement, progressListener);
|
||||
return findPath(factory, waypoints, maxIterations, allowDiagonalMovement, entityHeight, entityWidth, progressListener);
|
||||
}
|
||||
catch(PathfindingException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, runnable -> Bukkit.getScheduler().runTaskAsynchronously(NpcApi.plugin, runnable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously calculates a path through a list of waypoints.
|
||||
* Synchronously calculates a path through a list of waypoints using the default {@link NpcConfig#pathfinderFactory()}.
|
||||
* <p>
|
||||
* Each segment between consecutive waypoints is calculated in parallel using {@link CompletableFuture}. The returned future completes with a {@link Path}
|
||||
* containing the full path, or completes exceptionally if an {@link PathfindingException} occurs.
|
||||
* Segments are calculated sequentially. This method blocks the main thread and should be used with caution.
|
||||
*
|
||||
* @param waypoints the ordered list of locations to traverse
|
||||
* @param maxIterations the maximum number of iterations the A* algorithm will attempt per segment
|
||||
* @param allowDiagonalMovement whether diagonal movement is allowed
|
||||
* @param entityHeight the height of the entity traversing the path
|
||||
* @param entityWidth the width of the entity traversing the path
|
||||
* @param progressListener a progress listener with the signature (segmentIndex, totalSegments)
|
||||
* @return a {@link CompletableFuture} that completes with the calculated {@link Path}
|
||||
*/
|
||||
public static @NotNull CompletableFuture<Path> findPathAsync(@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
double entityHeight, double entityWidth,
|
||||
@Nullable BiConsumer<Integer, Integer> progressListener)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return findPath(waypoints, maxIterations, allowDiagonalMovement, entityHeight, entityWidth, progressListener);
|
||||
}
|
||||
catch(PathfindingException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, runnable -> Bukkit.getScheduler().runTaskAsynchronously(NpcApi.plugin, runnable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously calculates a path through a list of waypoints.
|
||||
* <p>
|
||||
* Each segment between consecutive waypoints is calculated in parallel internally using {@link CompletableFuture}, but this method blocks until all
|
||||
* segments are calculated and combined into a single {@link Path}.
|
||||
*
|
||||
* @param waypoints the ordered list of locations to traverse
|
||||
* @param maxIterations the maximum number of iterations the A* algorithm will attempt per segment
|
||||
* @param allowDiagonalMovement whether diagonal movement is allowed
|
||||
* @param progressListener a progress listener with the signature (segmentIndex, totalSegments)
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return the calculated {@link Path} containing all intermediate locations
|
||||
* @throws PathfindingException if any segment's start or end location is invalid/unwalkable
|
||||
*/
|
||||
public static @NotNull Path findPath(@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
@Nullable BiConsumer<Integer, Integer> progressListener) throws PathfindingException
|
||||
@Nullable BiConsumer<Double, Integer> progressListener) throws PathfindingException
|
||||
{
|
||||
return findPath(waypoints, maxIterations, allowDiagonalMovement, 1.8, 0.6, progressListener);
|
||||
return findPath(null, waypoints, maxIterations, allowDiagonalMovement, 1.8, 0.6, progressListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously calculates a path through a list of waypoints.
|
||||
* Synchronously calculates a path through a list of waypoints using a specified pathfinder factory.
|
||||
* <p>
|
||||
* Each segment between consecutive waypoints is calculated in parallel internally using {@link CompletableFuture}, but this method blocks until all
|
||||
* segments are calculated and combined into a single {@link Path}.
|
||||
* Segments are calculated sequentially. This method blocks the main thread and should be used with caution.
|
||||
*
|
||||
* @param factory the factory to create the pathfinder, if null the default factory {@link NpcConfig#pathfinderFactory()} will be used
|
||||
* @param waypoints the ordered list of locations to traverse
|
||||
* @param maxIterations the maximum number of iterations the A* algorithm will attempt per segment
|
||||
* @param maxIterations the maximum number of iterations the algorithm will attempt per segment
|
||||
* @param allowDiagonalMovement whether diagonal movement is allowed
|
||||
* @param entityHeight the height of the entity traversing the path
|
||||
* @param entityWidth the width of the entity traversing the path
|
||||
* @param progressListener a progress listener with the signature (segmentIndex, totalSegments)
|
||||
* @param progressListener A listener that receives a completion percentage between 0.0 and 1.0 and the current iteration count
|
||||
* @return the calculated {@link Path} containing all intermediate locations
|
||||
* @throws PathfindingException if any segment's start or end location is invalid/unwalkable
|
||||
*/
|
||||
public static @NotNull Path findPath(@NotNull List<Location> waypoints, int maxIterations, boolean allowDiagonalMovement,
|
||||
double entityHeight, double entityWidth, @Nullable BiConsumer<Integer, Integer> progressListener)
|
||||
public static @NotNull Path findPath(@Nullable AbstractPathfinder.PathfinderFactory factory, @NotNull List<Location> waypoints, int maxIterations,
|
||||
boolean allowDiagonalMovement, double entityHeight, double entityWidth,
|
||||
@Nullable BiConsumer<Double, Integer> progressListener)
|
||||
throws PathfindingException
|
||||
{
|
||||
if(waypoints.size() < 2)
|
||||
throw new IllegalArgumentException("Waypoints list must contain at least 2 locations.");
|
||||
|
||||
List<Location> fullPathPoints = new ArrayList<>();
|
||||
int totalSegments = waypoints.size() - 1;
|
||||
|
||||
AStarPathfinder aStar = new AStarPathfinder(maxIterations, allowDiagonalMovement, entityHeight, entityWidth);
|
||||
for(int i = 0; i < waypoints.size() - 1; i++)
|
||||
AbstractPathfinder pathfinder = (factory == null ? NpcApi.config.pathfinderFactory() : factory)
|
||||
.create(maxIterations, allowDiagonalMovement, entityHeight, entityWidth);
|
||||
|
||||
for(int i = 0; i < totalSegments; i++)
|
||||
{
|
||||
Location start = waypoints.get(i);
|
||||
Location end = waypoints.get(i + 1);
|
||||
|
||||
List<Location> segment = aStar.getPath(start, end);
|
||||
final int currentSegmentIndex = i;
|
||||
List<Location> segment = pathfinder.getPath(start, end, (segmentProgress, iterations) ->
|
||||
{
|
||||
if(progressListener != null)
|
||||
{
|
||||
double overallProgress = (currentSegmentIndex + segmentProgress) / totalSegments;
|
||||
progressListener.accept(overallProgress, iterations);
|
||||
}
|
||||
});
|
||||
|
||||
if(segment == null)
|
||||
throw new PathfindingException("Could not find path between waypoint " + i + " and " + (i + 1));
|
||||
|
||||
if(!fullPathPoints.isEmpty() && !segment.isEmpty())
|
||||
segment.remove(0);
|
||||
segment.removeFirst();
|
||||
|
||||
fullPathPoints.addAll(segment.stream().map(Location::clone).toList());
|
||||
|
||||
if(progressListener != null)
|
||||
progressListener.accept(i + 1, waypoints.size() - 1);
|
||||
progressListener.accept((currentSegmentIndex + 1.0) / totalSegments,0);
|
||||
}
|
||||
|
||||
return new Path(fullPathPoints, waypoints);
|
||||
@@ -154,4 +152,4 @@ public class PathfindingUtils
|
||||
return getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import de.eisi05.npc.api.enums.WalkingResult;
|
||||
import de.eisi05.npc.api.events.NpcStopWalkingEvent;
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.objects.NpcOption;
|
||||
import de.eisi05.npc.api.pathfinding.AStarPathfinder;
|
||||
import de.eisi05.npc.api.pathfinding.BoundingBoxPathfinder;
|
||||
import de.eisi05.npc.api.pathfinding.Path;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
import de.eisi05.npc.api.wrapper.packets.MoveEntityPacket;
|
||||
@@ -19,7 +19,6 @@ import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.Openable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -33,13 +32,12 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public class PathTask extends BukkitRunnable
|
||||
{
|
||||
private static final double gravity = -0.08;
|
||||
private static final double jumpVelocity = 0.5;
|
||||
private static final double terminalVelocity = -0.5;
|
||||
private static final double stepHeight = 0.55;
|
||||
private static final double GRAVITY = -0.08;
|
||||
private static final double JUMP_VELOCITY = 0.42;
|
||||
private static final double TERMINAL_VELOCITY = -0.5;
|
||||
private static final double STEP_HEIGHT = 0.55;
|
||||
|
||||
private final NPC npc;
|
||||
private final double entityHeight;
|
||||
private final double entityWidth;
|
||||
private final Path path;
|
||||
private final List<Location> pathPoints;
|
||||
@@ -73,7 +71,6 @@ public class PathTask extends BukkitRunnable
|
||||
{
|
||||
this.npc = builder.npc;
|
||||
double scale = npc.getOption(NpcOption.SCALE);
|
||||
this.entityHeight = npc.entity.getBoundingBox().getYSize() * scale;
|
||||
this.entityWidth = npc.entity.getBoundingBox().getXSize() * scale;
|
||||
this.path = builder.path;
|
||||
this.pathPoints = new ArrayList<>(builder.path.asLocations());
|
||||
@@ -106,24 +103,25 @@ public class PathTask extends BukkitRunnable
|
||||
public void run()
|
||||
{
|
||||
World world = npc.getLocation().getWorld();
|
||||
if(world == null) return;
|
||||
if(world == null)
|
||||
return;
|
||||
|
||||
int currentChunkX = currentPos.getBlockX() >> 4;
|
||||
int currentChunkZ = currentPos.getBlockZ() >> 4;
|
||||
|
||||
if (!world.isChunkLoaded(currentChunkX, currentChunkZ))
|
||||
if(!world.isChunkLoaded(currentChunkX, currentChunkZ))
|
||||
{
|
||||
handleUnloadedChunk(world, currentChunkX, currentChunkZ);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < pathPoints.size())
|
||||
if(index < pathPoints.size())
|
||||
{
|
||||
Location next = pathPoints.get(index);
|
||||
int nextChunkX = next.getBlockX() >> 4;
|
||||
int nextChunkZ = next.getBlockZ() >> 4;
|
||||
|
||||
if (!world.isChunkLoaded(nextChunkX, nextChunkZ))
|
||||
if(!world.isChunkLoaded(nextChunkX, nextChunkZ))
|
||||
{
|
||||
handleUnloadedChunk(world, nextChunkX, nextChunkZ);
|
||||
return;
|
||||
@@ -167,18 +165,8 @@ public class PathTask extends BukkitRunnable
|
||||
if(movement.lengthSquared() < 1e-6 && index < pathPoints.size() && currentPos.equals(target))
|
||||
return;
|
||||
|
||||
PhysicsResult physics = applyPhysics(movement, toTarget);
|
||||
|
||||
if(physics.skipHorizontal)
|
||||
{
|
||||
movement.setX(0);
|
||||
movement.setZ(0);
|
||||
}
|
||||
else if(physics.horizontalSlowdown < 1.0)
|
||||
movement.multiply(physics.horizontalSlowdown);
|
||||
|
||||
PhysicsResult physics = applyPhysics(movement);
|
||||
movement.setY(physics.yChange);
|
||||
|
||||
currentPos.add(movement);
|
||||
|
||||
float yaw, pitch;
|
||||
@@ -226,7 +214,7 @@ public class PathTask extends BukkitRunnable
|
||||
*/
|
||||
private void handleUnloadedChunk(World world, int chunkX, int chunkZ)
|
||||
{
|
||||
if (NpcApi.config.loadChunksOnPath() && !isWaitingForChunkLoad)
|
||||
if(NpcApi.config.loadChunksOnPath() && !isWaitingForChunkLoad)
|
||||
{
|
||||
isWaitingForChunkLoad = true;
|
||||
world.getChunkAt(chunkX, chunkZ);
|
||||
@@ -296,7 +284,6 @@ public class PathTask extends BukkitRunnable
|
||||
}
|
||||
|
||||
double distSq = Math.pow(door.getX() + 0.5 - currentPos.getX(), 2) + Math.pow(door.getZ() + 0.5 - currentPos.getZ(), 2);
|
||||
|
||||
if(distSq > 1.69)
|
||||
{
|
||||
if(openable.isOpen())
|
||||
@@ -327,6 +314,149 @@ public class PathTask extends BukkitRunnable
|
||||
openedDoors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the NPC has reached the current waypoint.
|
||||
*
|
||||
* @param toTarget The vector to the target waypoint
|
||||
* @return true if the waypoint has been reached, false otherwise
|
||||
*/
|
||||
private boolean hasReachedWaypoint(@NotNull Vector toTarget)
|
||||
{
|
||||
double horizontalDistSq = (toTarget.getX() * toTarget.getX()) + (toTarget.getZ() * toTarget.getZ());
|
||||
double verticalDiff = Math.abs(toTarget.getY());
|
||||
return horizontalDistSq <= 0.04 && verticalDiff < 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the horizontal movement vector for the NPC.
|
||||
*
|
||||
* @param toTarget The vector to the target waypoint
|
||||
* @param targetPoint The absolute target point
|
||||
* @return A vector representing the horizontal movement
|
||||
*/
|
||||
private @NotNull Vector calculateHorizontalMovement(@NotNull Vector toTarget, @NotNull Vector targetPoint)
|
||||
{
|
||||
Vector horizontal = new Vector(toTarget.getX(), 0, toTarget.getZ());
|
||||
double distSq = horizontal.lengthSquared();
|
||||
if(distSq < 1e-6)
|
||||
return new Vector(0, 0, 0);
|
||||
|
||||
double dist = Math.sqrt(distSq);
|
||||
double currentSpeed = speed;
|
||||
double yDiff = targetPoint.getY() - currentPos.getY();
|
||||
if(yDiff > STEP_HEIGHT && currentPos.getY() < targetPoint.getY())
|
||||
currentSpeed *= 0.6;
|
||||
|
||||
double moveDistance = Math.min(currentSpeed, dist);
|
||||
Vector moveStep = horizontal.clone().normalize().multiply(moveDistance);
|
||||
|
||||
if(Math.abs(moveDistance - dist) < 1e-6)
|
||||
{
|
||||
this.currentPos.setX(targetPoint.getX());
|
||||
this.currentPos.setZ(targetPoint.getZ());
|
||||
index++;
|
||||
return new Vector(0, 0, 0);
|
||||
}
|
||||
|
||||
return moveStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies physics (gravity, jumping) to the NPC's movement. Excludes strict bounding-box checks because the pathfinder guarantees a safe route.
|
||||
*
|
||||
* @param movement The current horizontal movement vector
|
||||
* @return A PhysicsResult containing the vertical movement and ground state
|
||||
*/
|
||||
private @NotNull PhysicsResult applyPhysics(Vector movement)
|
||||
{
|
||||
World world = npc.getLocation().getWorld();
|
||||
if(world == null)
|
||||
return new PhysicsResult(0, false);
|
||||
|
||||
Vector stepTarget = currentPos.clone().add(movement);
|
||||
double targetGroundY = getGroundY(world, stepTarget);
|
||||
|
||||
Location targetWaypoint = pathPoints.get(Math.min(index, pathPoints.size() - 1));
|
||||
if(targetGroundY < targetWaypoint.getY() - STEP_HEIGHT)
|
||||
targetGroundY = targetWaypoint.getY();
|
||||
|
||||
double currentGroundY = getGroundY(world, currentPos);
|
||||
boolean onGround = currentPos.getY() <= currentGroundY + 1e-5;
|
||||
double yChange;
|
||||
|
||||
if(onGround)
|
||||
{
|
||||
double yDiff = targetGroundY - currentPos.getY();
|
||||
|
||||
if(yDiff <= 0 && yDiff >= -STEP_HEIGHT)
|
||||
{
|
||||
verticalVelocity = 0;
|
||||
return new PhysicsResult(yDiff, true);
|
||||
}
|
||||
|
||||
if(yDiff > 0 && yDiff <= STEP_HEIGHT)
|
||||
{
|
||||
verticalVelocity = 0;
|
||||
return new PhysicsResult(yDiff, true);
|
||||
}
|
||||
|
||||
if(yDiff > STEP_HEIGHT)
|
||||
{
|
||||
verticalVelocity = JUMP_VELOCITY;
|
||||
return new PhysicsResult(JUMP_VELOCITY, false);
|
||||
}
|
||||
|
||||
verticalVelocity += GRAVITY;
|
||||
if(verticalVelocity < TERMINAL_VELOCITY)
|
||||
verticalVelocity = TERMINAL_VELOCITY;
|
||||
|
||||
return new PhysicsResult(verticalVelocity, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalVelocity += GRAVITY;
|
||||
if(verticalVelocity < TERMINAL_VELOCITY)
|
||||
verticalVelocity = TERMINAL_VELOCITY;
|
||||
|
||||
yChange = verticalVelocity;
|
||||
if(currentPos.getY() + yChange <= targetGroundY + 1e-5)
|
||||
{
|
||||
yChange = targetGroundY - currentPos.getY();
|
||||
verticalVelocity = 0;
|
||||
onGround = true;
|
||||
}
|
||||
|
||||
return new PhysicsResult(yChange, onGround);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the feet Y-coordinate of the ground at a given position. Delegates to {@link BoundingBoxPathfinder#resolveGroundSupport} so ground detection
|
||||
* uses the exact same full-footprint, collision-shape-based logic the pathfinder used when it planned the route.
|
||||
*
|
||||
* @param world The world to check in
|
||||
* @param pos The position to check
|
||||
* @return The Y-coordinate where the NPC's feet should be
|
||||
*/
|
||||
private double getGroundY(@NotNull World world, @NotNull Vector pos)
|
||||
{
|
||||
BoundingBoxPathfinder.FootSupport support = BoundingBoxPathfinder.resolveGroundSupport(world, pos.getX(), pos.getY(), pos.getZ(), entityWidth);
|
||||
|
||||
if(support.valid())
|
||||
return support.feetY();
|
||||
|
||||
Location currentWaypoint = pathPoints.get(Math.min(index, pathPoints.size() - 1));
|
||||
double highestY = world.getHighestBlockYAt(pos.getBlockX(), pos.getBlockZ());
|
||||
|
||||
if(Math.abs(currentWaypoint.getX() - pos.getX()) < 1.0 && Math.abs(currentWaypoint.getZ() - pos.getZ()) < 1.0)
|
||||
{
|
||||
if(highestY < currentWaypoint.getY() - 1.0)
|
||||
return currentWaypoint.getY();
|
||||
}
|
||||
|
||||
return highestY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the completion of the path. Performs final cleanup and calls the completion callback.
|
||||
*
|
||||
@@ -334,8 +464,7 @@ public class PathTask extends BukkitRunnable
|
||||
*/
|
||||
private boolean finishPath()
|
||||
{
|
||||
Location last = path.getWaypoints().isEmpty() ? null : path.getWaypoints().get(path.getWaypoints().size() - 1);
|
||||
|
||||
Location last = path.getWaypoints().isEmpty() ? null : path.getWaypoints().getLast();
|
||||
if(last != null)
|
||||
{
|
||||
if(currentPos.distanceSquared(last.toVector()) > 0.04)
|
||||
@@ -358,7 +487,7 @@ public class PathTask extends BukkitRunnable
|
||||
|
||||
if(event.changeRealLocation())
|
||||
{
|
||||
Location loc = path.getWaypoints().isEmpty() ? pathPoints.get(pathPoints.size() - 1) : path.getWaypoints().get(path.getWaypoints().size() - 1);
|
||||
Location loc = path.getWaypoints().isEmpty() ? pathPoints.getLast() : path.getWaypoints().getLast();
|
||||
npc.changeRealLocation(loc, getViewers());
|
||||
}
|
||||
|
||||
@@ -388,199 +517,6 @@ public class PathTask extends BukkitRunnable
|
||||
npc.sendNpcBodyPackets(body, getViewers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the NPC has reached the current waypoint.
|
||||
*
|
||||
* @param toTarget The vector to the target waypoint
|
||||
* @return true if the waypoint has been reached, false otherwise
|
||||
*/
|
||||
private boolean hasReachedWaypoint(@NotNull Vector toTarget)
|
||||
{
|
||||
double horizontalDistSq = (toTarget.getX() * toTarget.getX()) + (toTarget.getZ() * toTarget.getZ());
|
||||
double verticalDiff = Math.abs(toTarget.getY());
|
||||
return horizontalDistSq <= 0.04 && verticalDiff < 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the horizontal movement vector for the NPC.
|
||||
*
|
||||
* @param toTarget The vector to the target waypoint
|
||||
* @param targetPoint The absolute target point
|
||||
* @return A vector representing the horizontal movement
|
||||
*/
|
||||
private @NotNull Vector calculateHorizontalMovement(@NotNull Vector toTarget, @NotNull Vector targetPoint)
|
||||
{
|
||||
Vector horizontal = new Vector(toTarget.getX(), 0, toTarget.getZ());
|
||||
double distSq = horizontal.lengthSquared();
|
||||
if(distSq < 1e-6)
|
||||
return new Vector(0, 0, 0);
|
||||
|
||||
double dist = Math.sqrt(distSq);
|
||||
double moveDistance = Math.min(speed, dist);
|
||||
Vector moveStep = horizontal.clone().normalize().multiply(moveDistance);
|
||||
|
||||
if(Math.abs(moveDistance - dist) < 1e-6)
|
||||
{
|
||||
this.currentPos = targetPoint.clone();
|
||||
index++;
|
||||
return new Vector(0, 0, 0);
|
||||
}
|
||||
|
||||
return moveStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies physics (gravity, jumping, collision) to the NPC's movement.
|
||||
*
|
||||
* @param movement The current movement vector
|
||||
* @param toTarget The vector to the target waypoint
|
||||
* @return A PhysicsResult containing the vertical movement and ground state
|
||||
*/
|
||||
private @NotNull PhysicsResult applyPhysics(Vector movement, Vector toTarget)
|
||||
{
|
||||
World world = npc.getLocation().getWorld();
|
||||
if(world == null)
|
||||
return new PhysicsResult(0, false, false, 1.0);
|
||||
|
||||
double groundY = getGroundY(world, currentPos);
|
||||
boolean onGround = currentPos.getY() <= groundY + 1e-5;
|
||||
double yChange = 0;
|
||||
|
||||
if(onGround)
|
||||
{
|
||||
if(toTarget.getY() < 0 && Math.abs(toTarget.getY()) <= stepHeight)
|
||||
{
|
||||
Vector testPos = currentPos.clone().add(movement);
|
||||
testPos.setY(currentPos.getY() + toTarget.getY());
|
||||
if(isPositionValid(world, testPos))
|
||||
{
|
||||
yChange = toTarget.getY();
|
||||
verticalVelocity = 0;
|
||||
double slowdown = 0.5;
|
||||
return new PhysicsResult(yChange, true, false, slowdown);
|
||||
}
|
||||
}
|
||||
|
||||
if(toTarget.getY() > 0 && toTarget.getY() <= stepHeight && movement.lengthSquared() > 1e-6)
|
||||
{
|
||||
Vector testPos = currentPos.clone().add(movement);
|
||||
testPos.setY(currentPos.getY() + Math.min(toTarget.getY(), stepHeight));
|
||||
if(!isPositionValid(world, testPos))
|
||||
{
|
||||
yChange = Math.min(toTarget.getY(), stepHeight);
|
||||
verticalVelocity = 0;
|
||||
return new PhysicsResult(yChange, true, true, 1.0);
|
||||
}
|
||||
yChange = Math.min(toTarget.getY(), stepHeight);
|
||||
verticalVelocity = 0;
|
||||
return new PhysicsResult(yChange, true, false, 1.0);
|
||||
}
|
||||
else if(toTarget.getY() > 0.5)
|
||||
{
|
||||
Vector testPos = currentPos.clone().add(movement);
|
||||
testPos.setY(currentPos.getY() + jumpVelocity);
|
||||
if(!isPositionValid(world, testPos))
|
||||
{
|
||||
verticalVelocity = jumpVelocity;
|
||||
return new PhysicsResult(jumpVelocity, false, true, 1.0);
|
||||
}
|
||||
verticalVelocity = jumpVelocity;
|
||||
onGround = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalVelocity = 0;
|
||||
if(Math.abs(currentPos.getY() - groundY) > 1e-6)
|
||||
currentPos.setY(groundY);
|
||||
return new PhysicsResult(0, true, false, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
if(!onGround)
|
||||
{
|
||||
verticalVelocity += gravity;
|
||||
if(verticalVelocity < terminalVelocity)
|
||||
verticalVelocity = terminalVelocity;
|
||||
yChange = verticalVelocity;
|
||||
|
||||
Vector testPos = currentPos.clone().add(movement);
|
||||
testPos.setY(currentPos.getY() + yChange);
|
||||
if(!isPositionValid(world, testPos) && currentPos.getY() + yChange <= groundY + 0.1)
|
||||
{
|
||||
yChange = groundY - currentPos.getY();
|
||||
verticalVelocity = 0;
|
||||
onGround = true;
|
||||
}
|
||||
else if(currentPos.getY() + yChange <= groundY)
|
||||
{
|
||||
yChange = groundY - currentPos.getY();
|
||||
verticalVelocity = 0;
|
||||
onGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
return new PhysicsResult(yChange, onGround, false, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a position is valid (not inside a solid block).
|
||||
*
|
||||
* @param world The world to check in
|
||||
* @param pos The position to check
|
||||
* @return true if the position is valid, false otherwise
|
||||
*/
|
||||
private boolean isPositionValid(@NotNull World world, @NotNull Vector pos)
|
||||
{
|
||||
return AStarPathfinder.isPositionValid(world, pos.getX(), pos.getY(), pos.getZ(), entityHeight, entityWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the feet Y-coordinate of the ground at a given position.
|
||||
*
|
||||
* @param world The world to check in
|
||||
* @param pos The position to check
|
||||
* @return The Y-coordinate where the NPC's feet should be
|
||||
*/
|
||||
private double getGroundY(@NotNull World world, @NotNull Vector pos)
|
||||
{
|
||||
int bx = pos.getBlockX();
|
||||
int bz = pos.getBlockZ();
|
||||
int startY = pos.getBlockY();
|
||||
|
||||
for(int y = startY; y >= startY - 4; y--)
|
||||
{
|
||||
Block block = world.getBlockAt(bx, y, bz);
|
||||
|
||||
if(block.getBlockData() instanceof Openable || block.isLiquid())
|
||||
continue;
|
||||
|
||||
Collection<BoundingBox> boxes = block.getCollisionShape().getBoundingBoxes();
|
||||
if(boxes.isEmpty())
|
||||
continue;
|
||||
|
||||
double lx = pos.getX() - bx;
|
||||
double lz = pos.getZ() - bz;
|
||||
|
||||
double bestTop = -1.0;
|
||||
|
||||
for(BoundingBox bb : boxes)
|
||||
{
|
||||
if(lx >= bb.getMinX() && lx <= bb.getMaxX() && lz >= bb.getMinZ() && lz <= bb.getMaxZ())
|
||||
bestTop = Math.max(bestTop, bb.getMaxY());
|
||||
}
|
||||
|
||||
if(bestTop < 0.0)
|
||||
{
|
||||
for(BoundingBox bb : boxes)
|
||||
bestTop = Math.max(bestTop, bb.getMaxY());
|
||||
}
|
||||
|
||||
return y + bestTop;
|
||||
}
|
||||
|
||||
return world.getHighestBlockYAt(bx, bz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates smooth rotation for the NPC's head and body.
|
||||
*
|
||||
@@ -606,7 +542,7 @@ public class PathTask extends BukkitRunnable
|
||||
targetYaw = normalizeAngle(targetYaw);
|
||||
|
||||
float diff = normalizeAngle(targetYaw - previousYaw);
|
||||
diff = Math.max(-15f, Math.min(15f, diff));
|
||||
diff = Math.clamp(diff, -15f, 15f);
|
||||
|
||||
float yaw = previousYaw + diff;
|
||||
previousYaw = yaw;
|
||||
@@ -780,8 +716,8 @@ public class PathTask extends BukkitRunnable
|
||||
|
||||
if(event.changeRealLocation())
|
||||
{
|
||||
World world = path.getWaypoints().isEmpty() ? pathPoints.get(pathPoints.size() - 1).getWorld() :
|
||||
path.getWaypoints().get(path.getWaypoints().size() - 1).getWorld();
|
||||
World world = path.getWaypoints().isEmpty() ? pathPoints.getLast().getWorld() :
|
||||
path.getWaypoints().getLast().getWorld();
|
||||
Location loc = new Location(world, currentPos.getX(), currentPos.getY(), currentPos.getZ());
|
||||
npc.changeRealLocation(loc, getViewers());
|
||||
}
|
||||
@@ -801,12 +737,10 @@ public class PathTask extends BukkitRunnable
|
||||
/**
|
||||
* A record representing the result of physics calculations.
|
||||
*
|
||||
* @param yChange The vertical movement to apply
|
||||
* @param isGrounded Whether the NPC is on the ground
|
||||
* @param skipHorizontal Whether to skip horizontal movement this tick
|
||||
* @param horizontalSlowdown Factor to slow down horizontal movement (1.0 = normal, <1.0 = slower)
|
||||
* @param yChange The vertical movement to apply
|
||||
* @param isGrounded Whether the NPC is on the ground
|
||||
*/
|
||||
private record PhysicsResult(double yChange, boolean isGrounded, boolean skipHorizontal, double horizontalSlowdown) {}
|
||||
private record PhysicsResult(double yChange, boolean isGrounded) {}
|
||||
|
||||
// --- Builder Class ---
|
||||
|
||||
@@ -820,7 +754,7 @@ public class PathTask extends BukkitRunnable
|
||||
|
||||
private Player[] viewers = null;
|
||||
private Consumer<WalkingResult> callback = null;
|
||||
private double speed = 1.0;
|
||||
private double speed = 0.25;
|
||||
private boolean updateRealLocation = false;
|
||||
private boolean withRotation = true;
|
||||
private boolean autoManageWalkingViewers = false;
|
||||
@@ -921,4 +855,4 @@ public class PathTask extends BukkitRunnable
|
||||
return new PathTask(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,29 +16,44 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The {@link Tasks} class manages and starts various recurring tasks related to Non-Player Characters (NPCs) within the Bukkit environment. These tasks often
|
||||
* involve NPC behavior such as looking at nearby players.
|
||||
* involve NPC behavior such as looking at nearby players and updating skins via placeholders.
|
||||
*/
|
||||
public class Tasks
|
||||
{
|
||||
public static final Map<UUID, Map<UUID, String>> placeholderCache = new ConcurrentHashMap<>();
|
||||
private static final List<CompletableFuture<?>> activeFutures = new ArrayList<>();
|
||||
|
||||
// --- Skin Queue & Rate Limiting Constants ---
|
||||
private static final Map<String, Long> negativeCache = new ConcurrentHashMap<>();
|
||||
private static final long NEGATIVE_CACHE_DURATION = 300_000L; // 5 minutes
|
||||
private static final PriorityBlockingQueue<SkinRequest> fetchQueue = new PriorityBlockingQueue<>();
|
||||
private static final Map<String, SkinRequest> pendingRequests = new ConcurrentHashMap<>();
|
||||
private static final int MAX_TOKENS = 180;
|
||||
private static final AtomicInteger tokens = new AtomicInteger(MAX_TOKENS);
|
||||
|
||||
private static BukkitTask lookAtTask;
|
||||
private static BukkitTask placeholderTask;
|
||||
private static BukkitTask queueProcessorTask;
|
||||
|
||||
/**
|
||||
* Starts all defined NPC-related tasks. This method should be called when the plugin is enabled to ensure that NPC behaviors are active.
|
||||
* Starts all defined NPC-related tasks, including the skin fetch queue processor. This method should be called when the plugin is enabled to ensure that
|
||||
* NPC behaviors are active.
|
||||
*/
|
||||
public static void start()
|
||||
{
|
||||
lookAtTask();
|
||||
placeholderTask();
|
||||
startQueueProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all defined NPC-related tasks.
|
||||
* Stops all defined NPC-related tasks and cancels active skin requests.
|
||||
*/
|
||||
public static void stop()
|
||||
{
|
||||
@@ -48,16 +63,116 @@ public class Tasks
|
||||
if(placeholderTask != null && !placeholderTask.isCancelled())
|
||||
placeholderTask.cancel();
|
||||
|
||||
if(queueProcessorTask != null && !queueProcessorTask.isCancelled())
|
||||
queueProcessorTask.cancel();
|
||||
|
||||
lookAtTask = null;
|
||||
placeholderTask = null;
|
||||
queueProcessorTask = null;
|
||||
|
||||
List<CompletableFuture<?>> futuresToCancel;
|
||||
synchronized(activeFutures)
|
||||
{
|
||||
for(CompletableFuture<?> future : activeFutures)
|
||||
future.cancel(true);
|
||||
futuresToCancel = new ArrayList<>(activeFutures);
|
||||
activeFutures.clear();
|
||||
}
|
||||
|
||||
for(CompletableFuture<?> future : futuresToCancel)
|
||||
future.cancel(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a recurring task that makes NPCs look at nearby players. The task runs on a timer defined by {@code NpcApi.config.lookAtTimer()}. NPCs will
|
||||
* Starts the asynchronous token bucket rate limiter and queue consumer for skin fetches.
|
||||
*/
|
||||
private static void startQueueProcessor()
|
||||
{
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(NpcApi.plugin, () ->
|
||||
tokens.updateAndGet(current -> current < MAX_TOKENS ? Math.min(MAX_TOKENS, current + 3) : current), 20L, 20L);
|
||||
|
||||
queueProcessorTask = Bukkit.getScheduler().runTaskTimerAsynchronously(NpcApi.plugin, () ->
|
||||
{
|
||||
SkinRequest request = fetchQueue.peek();
|
||||
if(request == null)
|
||||
return;
|
||||
|
||||
int current;
|
||||
do
|
||||
{
|
||||
current = tokens.get();
|
||||
if(current <= 0)
|
||||
return;
|
||||
}
|
||||
while(!tokens.compareAndSet(current, current - 1));
|
||||
|
||||
fetchQueue.poll();
|
||||
if(request.cacheKey() != null && isNegativeCached(request.cacheKey()))
|
||||
{
|
||||
request.future().complete(Optional.empty());
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Skin> result = request.task().get();
|
||||
if(result.isEmpty() && request.cacheKey() != null)
|
||||
addNegativeCache(request.cacheKey());
|
||||
request.future().complete(result);
|
||||
}, 0L, 2L);
|
||||
}
|
||||
|
||||
private static boolean isNegativeCached(String identifier)
|
||||
{
|
||||
if(negativeCache.containsKey(identifier))
|
||||
{
|
||||
if(negativeCache.get(identifier) > System.currentTimeMillis())
|
||||
return true;
|
||||
negativeCache.remove(identifier);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addNegativeCache(String identifier)
|
||||
{
|
||||
if(identifier != null && !identifier.isBlank())
|
||||
negativeCache.put(identifier, System.currentTimeMillis() + NEGATIVE_CACHE_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a skin fetch task, combining futures if a request for the same key is already pending.
|
||||
*/
|
||||
private static CompletableFuture<Optional<Skin>> enqueueFetch(@NotNull String cacheKey, @NotNull Supplier<Optional<Skin>> task)
|
||||
{
|
||||
SkinRequest existing = pendingRequests.get(cacheKey);
|
||||
if(existing != null)
|
||||
return existing.future();
|
||||
|
||||
CompletableFuture<Optional<Skin>> future = new CompletableFuture<>();
|
||||
future.whenComplete((result, error) -> pendingRequests.remove(cacheKey));
|
||||
|
||||
SkinRequest request = new SkinRequest(cacheKey, task, future);
|
||||
pendingRequests.put(cacheKey, request);
|
||||
fetchQueue.add(request);
|
||||
|
||||
trackFuture(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fetches a skin by UUID using the rate-limited queue.
|
||||
*/
|
||||
public static CompletableFuture<Optional<Skin>> fetchSkinAsync(@NotNull UUID uuid)
|
||||
{
|
||||
return enqueueFetch(uuid.toString(), () -> Skin.fetchSkin(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously fetches a skin by name or URL using the rate-limited queue.
|
||||
*/
|
||||
public static CompletableFuture<Optional<Skin>> fetchSkinAsync(@NotNull String nameOrUrl)
|
||||
{
|
||||
return enqueueFetch(nameOrUrl, () -> Skin.fetchSkin(nameOrUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a recurring task that makes NPCs look at nearby players. The task runs on a timer defined by {@code NpcApi.config.getLookAtTimer()}. NPCs will
|
||||
* only look at players within a specified range, which is configured via {@link NpcOption#LOOK_AT_PLAYER}.
|
||||
*/
|
||||
private static void lookAtTask()
|
||||
@@ -126,7 +241,7 @@ public class Tasks
|
||||
|
||||
/**
|
||||
* Updates the skin of an NPC for a specific player based on a placeholder value. This method handles both UUID and string-based skin lookups, and updates
|
||||
* the NPC's skin asynchronously when the skin is fetched.
|
||||
* the NPC's skin asynchronously when the skin is fetched via the queue.
|
||||
*
|
||||
* @param player The player who will see the updated skin. Must not be null.
|
||||
* @param npc The NPC whose skin will be updated. Must not be null.
|
||||
@@ -135,26 +250,28 @@ public class Tasks
|
||||
*/
|
||||
public static void updateSkin(@NotNull Player player, @NotNull NPC npc, @NotNull NpcSkin npcSkin)
|
||||
{
|
||||
if(!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
return;
|
||||
|
||||
String newPlaceholder = (String) Reflections.invokeStaticMethod("me.clip.placeholderapi.PlaceholderAPI", "setPlaceholders", player,
|
||||
npcSkin.getPlaceholder()).get();
|
||||
|
||||
Map<UUID, String> playerCache = placeholderCache.getOrDefault(npc.getUUID(), new ConcurrentHashMap<>());
|
||||
Map<UUID, String> playerCache = placeholderCache.computeIfAbsent(npc.getUUID(), k -> new ConcurrentHashMap<>());
|
||||
String oldPlaceholder = playerCache.getOrDefault(player.getUniqueId(), null);
|
||||
if(newPlaceholder.equals(oldPlaceholder))
|
||||
return;
|
||||
|
||||
playerCache.put(player.getUniqueId(), newPlaceholder);
|
||||
placeholderCache.put(npc.getUUID(), playerCache);
|
||||
|
||||
try
|
||||
{
|
||||
UUID skinUuid = UUID.fromString(newPlaceholder);
|
||||
Skin.fetchSkinAsync(skinUuid).thenAccept(skinOpt -> skinOpt.ifPresent(skin ->
|
||||
fetchSkinAsync(skinUuid).thenAccept(skinOpt -> skinOpt.ifPresent(skin ->
|
||||
Bukkit.getScheduler().runTaskLater(NpcApi.plugin, () -> npc.updateSkin(player), 1)));
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
Skin.fetchSkinAsync(newPlaceholder).thenAccept(skinOpt -> skinOpt.ifPresent(skin ->
|
||||
fetchSkinAsync(newPlaceholder).thenAccept(skinOpt -> skinOpt.ifPresent(skin ->
|
||||
Bukkit.getScheduler().runTaskLater(NpcApi.plugin, () -> npc.updateSkin(player), 1)));
|
||||
}
|
||||
}
|
||||
@@ -166,16 +283,34 @@ public class Tasks
|
||||
*/
|
||||
public static void trackFuture(@NotNull CompletableFuture<?> future)
|
||||
{
|
||||
if(lookAtTask == null && placeholderTask == null)
|
||||
{
|
||||
future.cancel(true);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(activeFutures)
|
||||
{
|
||||
activeFutures.add(future);
|
||||
}
|
||||
future.whenComplete((result, error) ->
|
||||
{
|
||||
if(lookAtTask == null && placeholderTask == null)
|
||||
return;
|
||||
|
||||
synchronized(activeFutures)
|
||||
{
|
||||
activeFutures.remove(future);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private record SkinRequest(String cacheKey, Supplier<Optional<Skin>> task, CompletableFuture<Optional<Skin>> future) implements Comparable<SkinRequest>
|
||||
{
|
||||
@Override
|
||||
public int compareTo(@NotNull SkinRequest o)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package de.eisi05.npc.api.utils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link ObjectSaver} class provides utility methods for serializing and deserializing Java objects and lists of objects to and from a file. It uses
|
||||
* standard Java object serialization.
|
||||
*/
|
||||
public class ObjectSaver
|
||||
{
|
||||
private final static ObjectInputFilter objectInputFilter = filterInfo -> ObjectInputFilter.Status.ALLOWED;
|
||||
|
||||
private final File file;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ObjectSaver} instance, creating a {@link File} object from the given file path string. It ensures the parent directories exist
|
||||
* and creates the file if it doesn't already exist.
|
||||
*
|
||||
* @param file The path to the file where objects will be saved/loaded. Must not be {@code null}.
|
||||
* @throws RuntimeException If an {@link IOException} occurs while creating the file.
|
||||
*/
|
||||
public ObjectSaver(@NotNull String file)
|
||||
{
|
||||
this(new File(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ObjectSaver} instance with the specified {@link File} object. It ensures the parent directories of the file exist and creates the
|
||||
* file itself if it does not already exist.
|
||||
*
|
||||
* @param file The {@link File} object where objects will be saved/loaded. Must not be {@code null}.
|
||||
* @throws RuntimeException If an {@link IOException} occurs while creating the file.
|
||||
*/
|
||||
public ObjectSaver(@NotNull File file)
|
||||
{
|
||||
file.getParentFile().mkdirs();
|
||||
this.file = file;
|
||||
if(!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.createNewFile();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single serializable object to the file, appending to the file if it already exists. This is a convenience method that calls
|
||||
* {@link #write(Serializable, boolean)} with {@code append} set to {@code true}.
|
||||
*
|
||||
* @param object The object to write to the file. Must implement {@link Serializable}.
|
||||
* @param <T> The type of the object, which must extend {@link Serializable}.
|
||||
* @throws IOException If an I/O error occurs during writing.
|
||||
*/
|
||||
public <T extends Serializable> void write(T object) throws IOException
|
||||
{
|
||||
this.write(object, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single serializable object to the file.
|
||||
*
|
||||
* @param object The object to write to the file. Must implement {@link Serializable}.
|
||||
* @param append If {@code true}, the object will be appended to the end of the file. If {@code false}, the file will be truncated (its contents deleted)
|
||||
* before writing the new object.
|
||||
* @param <T> The type of the object, which must extend {@link Serializable}.
|
||||
* @throws IOException If an I/O error occurs during writing.
|
||||
*/
|
||||
public <T extends Serializable> void write(T object, boolean append) throws IOException
|
||||
{
|
||||
FileOutputStream fileOut = new FileOutputStream(this.file, append);
|
||||
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
|
||||
objectOut.writeObject(object);
|
||||
objectOut.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a list of serializable objects to the file, appending to the file if it already exists. This is a convenience method that calls
|
||||
* {@link #writeList(List, boolean)} with {@code append} set to {@code true}.
|
||||
*
|
||||
* @param object The list of objects to write to the file. Each object in the list must implement {@link Serializable}. Must not be {@code null}.
|
||||
* @param <T> The type of the objects in the list, which must extend {@link Serializable}.
|
||||
* @throws IOException If an I/O error occurs during writing.
|
||||
*/
|
||||
public <T extends Serializable> void writeList(@NotNull List<T> object) throws IOException
|
||||
{
|
||||
this.writeList(object, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a list of serializable objects to the file.
|
||||
*
|
||||
* @param object The list of objects to write to the file. Each object in the list must implement {@link Serializable}. Must not be {@code null}.
|
||||
* @param append If {@code true}, the list will be appended to the end of the file. If {@code false}, the file will be truncated (its contents deleted)
|
||||
* before writing the new list.
|
||||
* @param <T> The type of the objects in the list, which must extend {@link Serializable}.
|
||||
* @throws IOException If an I/O error occurs during writing.
|
||||
*/
|
||||
public <T extends Serializable> void writeList(@NotNull List<T> object, boolean append) throws IOException
|
||||
{
|
||||
FileOutputStream fileOut = new FileOutputStream(this.file, append);
|
||||
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
|
||||
objectOut.writeObject(object);
|
||||
objectOut.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single serializable object from the file.
|
||||
*
|
||||
* @param <T> The expected type of the object, which must extend {@link Serializable}.
|
||||
* @return The deserialized object, or {@code null} if an error occurs during reading (e.g., file not found, EOF, class not found, or I/O error).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Serializable> @Nullable T read()
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInputStream fileIn = new FileInputStream(this.file);
|
||||
ObjectInputStream objectOut = new ObjectInputStream(fileIn);
|
||||
objectOut.setObjectInputFilter(objectInputFilter);
|
||||
Object object = objectOut.readObject();
|
||||
objectOut.close();
|
||||
return (T) object;
|
||||
}
|
||||
catch(IOException | ClassNotFoundException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a list of serializable objects from the file.
|
||||
*
|
||||
* @param <T> The expected type of the objects in the list, which must extend {@link Serializable}.
|
||||
* @return The deserialized list of objects. Returns an empty {@link ArrayList} if an error occurs during reading (e.g., file not found, EOF, class not
|
||||
* found, or I/O error), or if the file is empty.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Serializable> @NotNull List<T> readList()
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInputStream fileIn = new FileInputStream(this.file);
|
||||
ObjectInputStream objectOut = new ObjectInputStream(fileIn);
|
||||
objectOut.setObjectInputFilter(objectInputFilter);
|
||||
Object object = objectOut.readObject();
|
||||
objectOut.close();
|
||||
return (List<T>) object;
|
||||
}
|
||||
catch(Exception var4)
|
||||
{
|
||||
return new ArrayList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,9 +397,9 @@ public class Reflections
|
||||
{
|
||||
if(this == o)
|
||||
return true;
|
||||
if(!(o instanceof MethodKey methodKey))
|
||||
if(!(o instanceof MethodKey(Class<?> clazz1, String name, Class<?>[] types)))
|
||||
return false;
|
||||
return clazz.equals(methodKey.clazz) && methodName.equals(methodKey.methodName) && Arrays.equals(paramTypes, methodKey.paramTypes);
|
||||
return clazz.equals(clazz1) && methodName.equals(name) && Arrays.equals(paramTypes, types);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -419,9 +419,9 @@ public class Reflections
|
||||
{
|
||||
if(this == o)
|
||||
return true;
|
||||
if(!(o instanceof FieldKey fieldKey))
|
||||
if(!(o instanceof FieldKey(Class<?> clazz1, String name)))
|
||||
return false;
|
||||
return clazz.equals(fieldKey.clazz) && fieldName.equals(fieldKey.fieldName);
|
||||
return clazz.equals(clazz1) && fieldName.equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -440,9 +440,9 @@ public class Reflections
|
||||
{
|
||||
if(this == o)
|
||||
return true;
|
||||
if(!(o instanceof ConstructorKey constructorKey))
|
||||
if(!(o instanceof ConstructorKey(Class<?> clazz1, Class<?>[] types)))
|
||||
return false;
|
||||
return clazz.equals(constructorKey.clazz) && Arrays.equals(paramTypes, constructorKey.paramTypes);
|
||||
return clazz.equals(clazz1) && Arrays.equals(paramTypes, types);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package de.eisi05.npc.api.utils;
|
||||
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.utils.serialize.NpcRegistry;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class holding a key-expression tuple referencing a predicate compiled or managed via {@link de.eisi05.npc.api.utils.serialize.NpcRegistry}.
|
||||
*
|
||||
* @param <T> the type of entity, location, or target evaluated against the NPC
|
||||
*/
|
||||
public class RegistryPredicate<T> implements SerializableBiPredicate<T, NPC>
|
||||
{
|
||||
protected final String key;
|
||||
protected final String expression;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code RegistryPredicate}.
|
||||
*
|
||||
* @param key the key identifying the condition or parser registered in {@link de.eisi05.npc.api.utils.serialize.NpcRegistry}
|
||||
* @param expression the raw condition expression string to be parsed or evaluated
|
||||
*/
|
||||
public RegistryPredicate(@NotNull String key, @Nullable String expression)
|
||||
{
|
||||
this.key = key;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key identifying the condition or parser
|
||||
*/
|
||||
@NotNull
|
||||
public String key()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the raw condition expression string
|
||||
*/
|
||||
@Nullable
|
||||
public String expression()
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates this predicate on the given arguments.
|
||||
*
|
||||
* @param object the primary target or context object being evaluated
|
||||
* @param object2 the target NPC
|
||||
* @return {@code true} if the predicate matches the given arguments, {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean test(T object, NPC object2)
|
||||
{
|
||||
if(object instanceof Location location)
|
||||
return NpcRegistry.compileCondition(key, expression).test(location, object2);
|
||||
else if(object instanceof LivingEntity entity)
|
||||
return NpcRegistry.compileTargetFilter(key, expression).test(entity, object2);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if(this == o)
|
||||
return true;
|
||||
if(o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
RegistryPredicate<?> that = (RegistryPredicate<?>) o;
|
||||
return Objects.equals(key, that.key) && Objects.equals(expression, that.expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(key, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "RegistryPredicate[" +
|
||||
"key=" + key + ", " +
|
||||
"expression=" + expression + ']';
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package de.eisi05.npc.api.utils;
|
||||
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedCommandSourceStack;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedComponent;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntitySnapshot;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -34,6 +38,23 @@ public class Var
|
||||
return Reflections.invokeStaticMethod("org.bukkit.craftbukkit.inventory.CraftItemStack", "asNMSCopy", itemStack).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a NMS ItemsSack to its Bukkit equivalent {@link ItemStack}. This method uses reflection to call the appropriate {@code asBukkitCopy} method from
|
||||
* the CraftBukkit library, which varies by server version.
|
||||
*
|
||||
* @param itemStack The NMS ItemStack to convert. Must not be {@code null}.
|
||||
* @return The bukkit {@link ItemStack}.
|
||||
* @throws java.util.NoSuchElementException If the reflection method is not found or cannot be invoked.
|
||||
*/
|
||||
public static @NotNull ItemStack fromNmsItemStack(@NotNull Object itemStack)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return (ItemStack) Reflections.invokeStaticMethod("org.bukkit.craftbukkit." + Versions.getVersion().getPath() + ".inventory.CraftItemStack",
|
||||
"asBukkitCopy", itemStack).get();
|
||||
|
||||
return (ItemStack) Reflections.invokeStaticMethod("org.bukkit.craftbukkit.inventory.CraftItemStack", "asBukkitCopy", itemStack).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Bukkit {@link EntityType} to its corresponding NMS (Netty Minecraft Server) entity type object. This is a utility method that uses reflection
|
||||
* to access the internal NMS classes.
|
||||
@@ -100,6 +121,63 @@ public class Var
|
||||
"fromComponent", component.getHandle()).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a json text into a {@link WrappedComponent} (NMS chat component equivalent). This method uses reflection to call the appropriate
|
||||
* {@code fromStringOrNull} method from CraftBukkit's CraftChatMessage utility, which handles parsing text into components.
|
||||
*
|
||||
* @param json The json text to convert. Must not be {@code null}.
|
||||
* @return A {@link WrappedComponent} representing the given text.
|
||||
* @throws java.util.NoSuchElementException If the reflection method is not found or cannot be invoked.
|
||||
*/
|
||||
public static @NotNull WrappedComponent fromJson(@NotNull String json)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return WrappedComponent.fromHandle(
|
||||
Reflections.invokeStaticMethod("org.bukkit.craftbukkit." + Versions.getVersion().getPath() + ".util.CraftChatMessage",
|
||||
"fromJSONOrNull", json).get());
|
||||
return WrappedComponent.fromHandle(
|
||||
Reflections.invokeStaticMethod("org.bukkit.craftbukkit.util.CraftChatMessage",
|
||||
"fromJSONOrNull", json).get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WrappedComponent} (NMS chat component equivalent) back into json format. This method uses reflection to call the appropriate
|
||||
* {@code fromComponent} method from CraftBukkit's CraftChatMessage utility.
|
||||
*
|
||||
* @param component The {@link WrappedComponent} to convert to json. Must not be {@code null}.
|
||||
* @return A json representation of the component.
|
||||
* @throws java.util.NoSuchElementException If the reflection method is not found or cannot be invoked.
|
||||
*/
|
||||
public static @NotNull String toJson(@NotNull WrappedComponent component)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return (String) Reflections.invokeStaticMethod("org.bukkit.craftbukkit." + Versions.getVersion().getPath() + ".util.CraftChatMessage",
|
||||
"toJSON", component.getHandle()).get();
|
||||
return (String) Reflections.invokeStaticMethod("org.bukkit.craftbukkit.util.CraftChatMessage",
|
||||
"toJSON", component.getHandle()).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and updates a {@link WrappedComponent} in the context of a specific entity and command source stack. This method handles version differences in
|
||||
* NMS chat component utilities, using reflection to interact with Minecraft's internal component resolution mechanisms.
|
||||
*
|
||||
* @param sourceStack The {@link WrappedCommandSourceStack} providing the context for the resolution. Can be {@code null}.
|
||||
* @param component The {@link WrappedComponent} to update. Must not be {@code null}.
|
||||
* @param entity The {@link WrappedEntity} acting as an entity override during resolution. Can be {@code null}.
|
||||
* @param depth The recursion depth limit for resolving nested components or selectors.
|
||||
* @return A new {@link WrappedComponent} with updated and resolved content.
|
||||
* @throws java.util.NoSuchElementException If the reflection method or necessary classes are not found or cannot be invoked.
|
||||
*/
|
||||
public static @NotNull WrappedComponent updateForEntity(@Nullable WrappedCommandSourceStack sourceStack,
|
||||
@NotNull WrappedComponent component, @Nullable WrappedEntity<?> entity, int depth)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return WrappedComponent.fromHandle(Reflections.invokeStaticMethod("net.minecraft.network.chat.ChatComponentUtils",
|
||||
"a", sourceStack.getHandle(), component.getHandle(), entity.getHandle(), depth).get());
|
||||
return WrappedComponent.fromHandle(Reflections.invokeStaticMethod("net.minecraft.network.chat.ComponentUtils",
|
||||
"resolve", new ResolutionContextBuilder().withSource(sourceStack).withEntityOverride(entity).build(), component.getHandle(), depth).get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an unchecked cast of an object to a specified type. This method can be used to bypass Java's type checking at compile time, but it comes with
|
||||
* the risk of {@link ClassCastException} at runtime if the object is not an instance of the target type.
|
||||
@@ -218,4 +296,36 @@ public class Var
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ResolutionContext$Builder")
|
||||
private static class ResolutionContextBuilder extends Wrapper
|
||||
{
|
||||
private ResolutionContextBuilder()
|
||||
{
|
||||
super(createInstance(ResolutionContextBuilder.class));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withSource")
|
||||
public @NotNull ResolutionContextBuilder withSource(@Nullable WrappedCommandSourceStack sourceStack)
|
||||
{
|
||||
if(sourceStack == null)
|
||||
return this;
|
||||
|
||||
invokeWrappedMethod(sourceStack.getHandle());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withEntityOverride")
|
||||
public @NotNull ResolutionContextBuilder withEntityOverride(@Nullable WrappedEntity<?> entityOverride)
|
||||
{
|
||||
invokeWrappedMethod(entityOverride == null ? null : entityOverride.getHandle());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "build")
|
||||
public @NotNull Object build()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The {@link Versions} enum represents the supported Minecraft server versions
|
||||
* and provides utility methods for working with version-specific paths and comparisons.
|
||||
* It helps in adapting the plugin's functionality to different server environments.
|
||||
* The {@link Versions} enum represents the supported Minecraft server versions and provides utility methods for working with version-specific paths and
|
||||
* comparisons. It helps in adapting the plugin's functionality to different server environments.
|
||||
*/
|
||||
public enum Versions
|
||||
{
|
||||
@@ -22,42 +21,52 @@ public enum Versions
|
||||
/**
|
||||
* Minecraft 1.17 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_17("v1_17_R1"),
|
||||
/**
|
||||
* Minecraft 1.18 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_18("v1_18_R1"),
|
||||
/**
|
||||
* Minecraft 1.18.2 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_18_2("v1_18_R2"),
|
||||
/**
|
||||
* Minecraft 1.19 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_19("v1_19_R1"),
|
||||
/**
|
||||
* Minecraft 1.19.1 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_19_1("v1_19_R1"),
|
||||
/**
|
||||
* Minecraft 1.19.3 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_19_3("v1_19_R2"),
|
||||
/**
|
||||
* Minecraft 1.19.4 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_19_4("v1_19_R3"),
|
||||
/**
|
||||
* Minecraft 1.20 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_20("v1_20_R1"),
|
||||
/**
|
||||
* Minecraft 1.20.2 version.
|
||||
*/
|
||||
@Deprecated(since = "2.4.0")
|
||||
V1_20_2("v1_20_R2"),
|
||||
/**
|
||||
* Minecraft 1.20.4 version.
|
||||
*/
|
||||
@Deprecated
|
||||
V1_20_4("v1_20_R3"),
|
||||
/**
|
||||
* Minecraft 1.20.6 version.
|
||||
@@ -94,7 +103,15 @@ public enum Versions
|
||||
/**
|
||||
* Minecraft 26.1 version.
|
||||
*/
|
||||
V26_1("v26_1");
|
||||
V26_1("v26_1"),
|
||||
/**
|
||||
* Minecraft 26.2 version.
|
||||
*/
|
||||
V26_2("v26_2"),
|
||||
/**
|
||||
* Minecraft 26.3 version.
|
||||
*/
|
||||
V26_3("v26_3");
|
||||
|
||||
/**
|
||||
* Caches the determined current server version to avoid repeated lookups.
|
||||
@@ -102,8 +119,7 @@ public enum Versions
|
||||
private static Versions VERSION;
|
||||
|
||||
/**
|
||||
* The NMS (Net Minecraft Server) path component corresponding to this version.
|
||||
* For example, "v1_17_R1" for Minecraft 1.17.
|
||||
* The NMS (Net Minecraft Server) path component corresponding to this version. For example, "v1_17_R1" for Minecraft 1.17.
|
||||
*/
|
||||
private final String path;
|
||||
|
||||
@@ -118,9 +134,8 @@ public enum Versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines and returns the current Minecraft server version based on the Bukkit server's package name.
|
||||
* The determined version is cached for later calls. If the version is not recognized,
|
||||
* the plugin will be disabled and a {@link VersionNotFound} exception will be thrown.
|
||||
* Determines and returns the current Minecraft server version based on the Bukkit server's package name. The determined version is cached for later calls.
|
||||
* If the version is not recognized, the plugin will be disabled and a {@link VersionNotFound} exception will be thrown.
|
||||
*
|
||||
* @return The {@link Versions} enum entry corresponding to the current server version. Must not be {@code null}.
|
||||
* @throws VersionNotFound If the current server version cannot be determined or is not supported.
|
||||
@@ -130,13 +145,32 @@ public enum Versions
|
||||
if(VERSION != null)
|
||||
return VERSION;
|
||||
|
||||
if(Bukkit.getServer().getBukkitVersion().startsWith("26.1"))
|
||||
return V26_1;
|
||||
VERSION = switch(Bukkit.getServer().getBukkitVersion())
|
||||
{
|
||||
case String v when v.startsWith("26.1") -> V26_1;
|
||||
case String v when v.startsWith("26.2") -> V26_2;
|
||||
case String v when v.startsWith("26.3") -> V26_3;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
if(VERSION != null)
|
||||
return VERSION;
|
||||
|
||||
String nmsVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
return VERSION = Arrays.stream(Versions.values())
|
||||
.filter(v -> v.path.equalsIgnoreCase(nmsVersion))
|
||||
.filter(versions ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return !Versions.class.getDeclaredField(versions.name()).isAnnotationPresent(Deprecated.class);
|
||||
}
|
||||
catch(NoSuchFieldException e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.reduce((a, b) -> Bukkit.getBukkitVersion().startsWith("1.19.1") ? Versions.V1_19_1 : Versions.V1_19)
|
||||
.orElseThrow(() ->
|
||||
{
|
||||
@@ -146,9 +180,8 @@ public enum Versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@link Versions} enum entries that fall inclusively between
|
||||
* two specified versions (based on their ordinal values).
|
||||
* The {@code NONE} version is excluded from the result.
|
||||
* Returns an array of {@link Versions} enum entries that fall inclusively between two specified versions (based on their ordinal values). The {@code NONE}
|
||||
* version is excluded from the result.
|
||||
*
|
||||
* @param versions1 The starting version (inclusive). Must not be {@code null}.
|
||||
* @param versions2 The ending version (inclusive). Must not be {@code null}.
|
||||
@@ -163,8 +196,7 @@ public enum Versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current server version is numerically smaller than a specified version.
|
||||
* This comparison is based on the ordinal value of the enum entries.
|
||||
* Checks if the current server version is numerically smaller than a specified version. This comparison is based on the ordinal value of the enum entries.
|
||||
*
|
||||
* @param versions The version to compare against. Must not be {@code null}.
|
||||
* @return {@code true} if the current version is smaller, {@code false} otherwise.
|
||||
@@ -175,8 +207,7 @@ public enum Versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current server version is contained within a given {@link Mapping}.
|
||||
* A mapping can specify either a fixed version or a range of versions.
|
||||
* Checks if the current server version is contained within a given {@link Mapping}. A mapping can specify either a fixed version or a range of versions.
|
||||
*
|
||||
* @param mapping The {@link Mapping} to check against. Must not be {@code null}.
|
||||
* @return {@code true} if the current version matches the mapping, {@code false} otherwise.
|
||||
@@ -200,8 +231,7 @@ public enum Versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the version, with underscores replaced by dots for better readability.
|
||||
* For example, {@code V1_17} becomes "V1.17".
|
||||
* Returns the name of the version, with underscores replaced by dots for better readability. For example, {@code V1_17} becomes "V1.17".
|
||||
*
|
||||
* @return The formatted name of the version as a {@link String}. Must not be {@code null}.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package de.eisi05.npc.api.utils.serialize;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GsonDebugFactory implements TypeAdapterFactory
|
||||
{
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
|
||||
{
|
||||
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
||||
|
||||
return new TypeAdapter<T>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, T value) throws IOException
|
||||
{
|
||||
delegate.write(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(JsonReader in) throws IOException
|
||||
{
|
||||
System.out.println("[Gson Trace] Deserializing type: " + type.getType());
|
||||
T result = delegate.read(in);
|
||||
if(result != null)
|
||||
System.out.println("[Gson Trace] -> Resolved into runtime class: " + result.getClass().getName());
|
||||
return result;
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
package de.eisi05.npc.api.utils;
|
||||
package de.eisi05.npc.api.utils.serialize;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
@@ -8,10 +8,10 @@ import org.bukkit.util.io.BukkitObjectInputStream;
|
||||
import org.bukkit.util.io.BukkitObjectOutputStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* The {@link ItemSerializer} class provides utility methods for serializing and deserializing
|
||||
@@ -96,7 +96,7 @@ public class ItemSerializer
|
||||
dataOutput.writeObject(item);
|
||||
|
||||
dataOutput.close();
|
||||
return Base64Coder.encodeLines(outputStream.toByteArray());
|
||||
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
|
||||
} catch(Exception e)
|
||||
{
|
||||
return null;
|
||||
@@ -123,7 +123,7 @@ public class ItemSerializer
|
||||
dataOutput.writeObject(item);
|
||||
|
||||
dataOutput.close();
|
||||
return Base64Coder.encodeLines(outputStream.toByteArray());
|
||||
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
|
||||
} catch(Exception e)
|
||||
{
|
||||
return "";
|
||||
@@ -157,7 +157,7 @@ public class ItemSerializer
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getMimeDecoder().decode(data));
|
||||
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
|
||||
Inventory inventory = Bukkit.getServer().createInventory(null, dataInput.readInt());
|
||||
|
||||
@@ -183,7 +183,7 @@ public class ItemSerializer
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getMimeDecoder().decode(data));
|
||||
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
|
||||
ItemStack[] items = new ItemStack[dataInput.readInt()];
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package de.eisi05.npc.api.utils.serialize;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import de.eisi05.npc.api.interfaces.NpcClickAction;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
public class NpcClickActionAdapter extends TypeAdapter<NpcClickAction>
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, NpcClickAction value) throws IOException
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> clazz = value.getClass();
|
||||
out.beginObject();
|
||||
|
||||
if(clazz.isSynthetic() || clazz.getName().contains("$$Lambda$") || !Serializable.class.isAssignableFrom(clazz))
|
||||
{
|
||||
try(ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objOut = new ObjectOutputStream(byteOut))
|
||||
{
|
||||
objOut.writeObject(value);
|
||||
String base64Bytes = Base64.getEncoder().encodeToString(byteOut.toByteArray());
|
||||
out.name("bytes").value(base64Bytes);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new IOException("Failed to natively serialize dynamic NpcClickAction lambda expression", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.name("className").value(clazz.getName());
|
||||
|
||||
JsonElement delegateElement = ObjectSaver.CLEAN_GSON.toJsonTree(value, clazz);
|
||||
if(delegateElement.isJsonObject())
|
||||
{
|
||||
JsonObject jsonObject = delegateElement.getAsJsonObject();
|
||||
for(Map.Entry<String, JsonElement> entry : jsonObject.entrySet())
|
||||
{
|
||||
if(entry.getKey().equals("className") || entry.getKey().equals("bytes"))
|
||||
continue;
|
||||
|
||||
out.name(entry.getKey());
|
||||
ObjectSaver.CLEAN_GSON.toJson(entry.getValue(), out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpcClickAction read(JsonReader in) throws IOException
|
||||
{
|
||||
if(in.peek() == com.google.gson.stream.JsonToken.NULL)
|
||||
{
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject();
|
||||
|
||||
if(jsonObject.has("bytes"))
|
||||
{
|
||||
String base64Bytes = jsonObject.get("bytes").getAsString();
|
||||
byte[] bytes = Base64.getDecoder().decode(base64Bytes);
|
||||
|
||||
try(ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
|
||||
ObjectInputStream objIn = new ObjectInputStream(byteIn))
|
||||
{
|
||||
NpcClickAction action = (NpcClickAction) objIn.readObject();
|
||||
if(action != null)
|
||||
return action.initialize();
|
||||
return action;
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
{
|
||||
throw new JsonParseException("Failed to map compiled runtime lambda target context dependencies.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if(jsonObject.has("className"))
|
||||
{
|
||||
String className = jsonObject.get("className").getAsString();
|
||||
try
|
||||
{
|
||||
Class<?> targetClass = Class.forName(className);
|
||||
NpcClickAction action = (NpcClickAction) ObjectSaver.CLEAN_GSON.fromJson(jsonObject, targetClass);
|
||||
|
||||
if(action != null)
|
||||
return action.initialize();
|
||||
|
||||
return action;
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
{
|
||||
throw new JsonParseException("Could not track down registered class definition: " + className, e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new JsonParseException("Invalid NpcClickAction JSON object structure. Must contain either 'className' or 'bytes'.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package de.eisi05.npc.api.utils.serialize;
|
||||
|
||||
import de.eisi05.npc.api.objects.NPC;
|
||||
import de.eisi05.npc.api.objects.Skin;
|
||||
import de.eisi05.npc.api.utils.SerializableBiPredicate;
|
||||
import de.eisi05.npc.api.utils.TriFunction;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedComponent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Registry class for managing dynamic callbacks, functions, and expression parsers associated with NPC components (e.g., name formatting, skin generation, goal
|
||||
* conditions, and targeting filters).
|
||||
*/
|
||||
public final class NpcRegistry
|
||||
{
|
||||
/**
|
||||
* Key identifier for PlaceholderAPI-backed functions.
|
||||
*/
|
||||
public static final String KEY_PLACEHOLDER_API = "placeholder";
|
||||
|
||||
/**
|
||||
* Key identifier for expression-based condition parsers.
|
||||
*/
|
||||
public static final String EXPRESSION_PARSER = "expression";
|
||||
|
||||
private static final Map<String, BiFunction<Player, String, WrappedComponent>> NAME_FUNCTIONS = new ConcurrentHashMap<>();
|
||||
private static final Map<String, TriFunction<Player, NPC, String, Skin>> SKIN_FUNCTIONS = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Function<String, SerializableBiPredicate<Location, NPC>>> CONDITION_PARSERS = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Function<String, SerializableBiPredicate<LivingEntity, NPC>>> TARGET_FILTER_PARSERS = new ConcurrentHashMap<>();
|
||||
|
||||
private NpcRegistry() {}
|
||||
|
||||
// ==========================================
|
||||
// Name Functions
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Registers a function used to dynamically compute dynamic name components for players viewing an NPC.
|
||||
*
|
||||
* @param key the lookup key identifying the function
|
||||
* @param function the function producing a {@link WrappedComponent} given a viewer and placeholder key
|
||||
* @return {@code true} if successfully registered; {@code false} if a mapping already exists for the key
|
||||
*/
|
||||
public static boolean registerNameFunction(@NotNull String key, @NotNull BiFunction<Player, String, WrappedComponent> function)
|
||||
{
|
||||
return NAME_FUNCTIONS.putIfAbsent(key.toLowerCase(), function) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a registered name function by key.
|
||||
*
|
||||
* @param key the lookup key
|
||||
* @return the associated {@link BiFunction}, or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable BiFunction<Player, String, WrappedComponent> getNameFunction(@NotNull String key)
|
||||
{
|
||||
return NAME_FUNCTIONS.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Skin Functions
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Registers a function used to dynamic resolve NPC skin properties at runtime.
|
||||
*
|
||||
* @param key the lookup key identifying the skin resolver
|
||||
* @param function the function producing a {@link Skin} given a viewer, target NPC, and placeholder key
|
||||
* @return {@code true} if successfully registered; {@code false} if a mapping already exists for the key
|
||||
*/
|
||||
public static boolean registerSkinFunction(@NotNull String key, @NotNull TriFunction<Player, NPC, String, Skin> function)
|
||||
{
|
||||
return SKIN_FUNCTIONS.putIfAbsent(key.toLowerCase(), function) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a registered skin function by key.
|
||||
*
|
||||
* @param key the lookup key
|
||||
* @return the associated {@link TriFunction}, or {@code null} if not found
|
||||
*/
|
||||
public static @Nullable TriFunction<Player, NPC, String, Skin> getSkinFunction(@NotNull String key)
|
||||
{
|
||||
return SKIN_FUNCTIONS.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Condition Parsers
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Registers a function capable of parsing conditional expressions into location/NPC predicates.
|
||||
*
|
||||
* @param key the lookup key
|
||||
* @param parser the parser function translating string expressions into a {@link SerializableBiPredicate}
|
||||
* @return {@code true} if successfully registered; {@code false} if a mapping already exists for the key
|
||||
*/
|
||||
public static boolean registerConditionParser(@NotNull String key, @NotNull Function<String, SerializableBiPredicate<Location, NPC>> parser)
|
||||
{
|
||||
return CONDITION_PARSERS.putIfAbsent(key.toLowerCase(), parser) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a function capable of parsing expression strings into living entity targeting filter predicates.
|
||||
*
|
||||
* @param key the lookup key
|
||||
* @param parser the parser function translating string expressions into a {@link SerializableBiPredicate}
|
||||
* @return {@code true} if successfully registered; {@code false} if a mapping already exists for the key
|
||||
*/
|
||||
public static boolean registerTargetFilterParser(@NotNull String key, @NotNull Function<String, SerializableBiPredicate<LivingEntity, NPC>> parser)
|
||||
{
|
||||
return TARGET_FILTER_PARSERS.putIfAbsent(key.toLowerCase(), parser) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a conditional expression string into a location predicate using a registered condition parser.
|
||||
*
|
||||
* @param key the condition parser registration key
|
||||
* @param expression the string expression to compile
|
||||
* @return the compiled {@link SerializableBiPredicate}, or a default predicate returning {@code false} if parser missing
|
||||
*/
|
||||
public static @Nullable SerializableBiPredicate<Location, NPC> compileCondition(@NotNull String key, @Nullable String expression)
|
||||
{
|
||||
Function<String, SerializableBiPredicate<Location, NPC>> parser = CONDITION_PARSERS.get(key.toLowerCase());
|
||||
return parser != null ? parser.apply(expression) : (loc, npc) -> false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a targeting filter expression string into an entity predicate using a registered target filter parser.
|
||||
*
|
||||
* @param key the target filter parser registration key
|
||||
* @param expression the string expression to compile
|
||||
* @return the compiled {@link SerializableBiPredicate}, or a default predicate returning {@code false} if parser missing
|
||||
*/
|
||||
public static @Nullable SerializableBiPredicate<LivingEntity, NPC> compileTargetFilter(@NotNull String key, @Nullable String expression)
|
||||
{
|
||||
Function<String, SerializableBiPredicate<LivingEntity, NPC>> parser = TARGET_FILTER_PARSERS.get(key.toLowerCase());
|
||||
return parser != null ? parser.apply(expression) : (loc, npc) -> false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package de.eisi05.npc.api.utils.serialize;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import de.eisi05.npc.api.ai.Goal;
|
||||
import de.eisi05.npc.api.interfaces.NpcClickAction;
|
||||
import de.eisi05.npc.api.objects.NpcOption;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
public class ObjectSaver
|
||||
{
|
||||
private static final TypeAdapter<Instant> INSTANT_ADAPTER = new TypeAdapter<Instant>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, Instant value) throws IOException
|
||||
{
|
||||
if(value == null)
|
||||
out.nullValue();
|
||||
else
|
||||
out.value(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant read(JsonReader in) throws IOException
|
||||
{
|
||||
if(in.peek() == JsonToken.NULL)
|
||||
{
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
return Instant.parse(in.nextString());
|
||||
}
|
||||
}.nullSafe();
|
||||
|
||||
private static final TypeAdapter<ItemStack> ITEM_STACK_ADAPTER = new TypeAdapter<ItemStack>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, ItemStack value) throws IOException
|
||||
{
|
||||
if(value == null || value.getType().isAir() || value.getAmount() <= 0)
|
||||
{
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("item", value);
|
||||
|
||||
String yamlString = config.saveToString();
|
||||
Map<String, Object> nativeMap = new org.yaml.snakeyaml.Yaml().load(yamlString);
|
||||
Object itemStructure = nativeMap.get("item");
|
||||
|
||||
JsonElement element = CLEAN_GSON.toJsonTree(itemStructure);
|
||||
CLEAN_GSON.toJson(element, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack read(JsonReader in) throws IOException
|
||||
{
|
||||
if(in.peek() == JsonToken.NULL)
|
||||
{
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonElement element = Streams.parse(in);
|
||||
if(!element.isJsonObject())
|
||||
return null;
|
||||
|
||||
JsonObject jsonObject = element.getAsJsonObject();
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
try
|
||||
{
|
||||
config.loadFromString("item: " + CLEAN_GSON.toJson(jsonObject));
|
||||
}
|
||||
catch(InvalidConfigurationException e)
|
||||
{
|
||||
throw new IOException("Failed to parse JSON into Bukkit Configuration", e);
|
||||
}
|
||||
|
||||
return config.getItemStack("item");
|
||||
}
|
||||
}.nullSafe();
|
||||
|
||||
private static final TypeAdapter<Serializable> SERIALIZABLE_ADAPTER = new TypeAdapter<>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, Serializable value) throws IOException
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
CLEAN_GSON.toJson(value, value.getClass(), out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read(JsonReader in)
|
||||
{
|
||||
JsonElement element = JsonParser.parseReader(in);
|
||||
return (Serializable) CLEAN_GSON.fromJson(element, Object.class);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Gson CLEAN_GSON = new GsonBuilder()
|
||||
//.registerTypeAdapterFactory(new GsonDebugFactory())
|
||||
.registerTypeAdapter(Instant.class, INSTANT_ADAPTER)
|
||||
.registerTypeHierarchyAdapter(ItemStack.class, ITEM_STACK_ADAPTER)
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
static final Gson GSON = new GsonBuilder()
|
||||
//.registerTypeAdapterFactory(new GsonDebugFactory())
|
||||
.registerTypeAdapter(Instant.class, INSTANT_ADAPTER)
|
||||
.registerTypeHierarchyAdapter(ItemStack.class, ITEM_STACK_ADAPTER)
|
||||
.registerTypeAdapter(Serializable.class, SERIALIZABLE_ADAPTER)
|
||||
.registerTypeHierarchyAdapter(NpcClickAction.class, new NpcClickActionAdapter())
|
||||
.registerTypeHierarchyAdapter(Goal.class, (JsonDeserializer<Goal>) (json, typeOfT, context) ->
|
||||
{
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
if(jsonObject.entrySet().isEmpty())
|
||||
return null;
|
||||
Map.Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
|
||||
String goalKey = entry.getKey();
|
||||
JsonObject goalData = entry.getValue().getAsJsonObject();
|
||||
String className = goalKey.substring(0, 1).toUpperCase() + goalKey.substring(1) + "Goal";
|
||||
try
|
||||
{
|
||||
Class<?> clazz = Class.forName("de.eisi05.npc.api.ai.goals." + className);
|
||||
return (Goal) CLEAN_GSON.fromJson(goalData, clazz);
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (Goal) CLEAN_GSON.fromJson(goalData, Class.forName(goalKey));
|
||||
}
|
||||
catch(ClassNotFoundException ex)
|
||||
{
|
||||
throw new JsonParseException("Unknown goal: " + className + " (" + goalKey + ")", ex);
|
||||
}
|
||||
}
|
||||
})
|
||||
.registerTypeHierarchyAdapter(Goal.class, (JsonSerializer<Goal>) (src, typeOfSrc, context) ->
|
||||
{
|
||||
JsonObject wrapper = new JsonObject();
|
||||
String shortName = src.getClass().getSimpleName().replace("Goal", "");
|
||||
String jsonKey = shortName.isEmpty() ? src.getClass().getName() : shortName.substring(0, 1).toLowerCase() + shortName.substring(1);
|
||||
wrapper.add(jsonKey, CLEAN_GSON.toJsonTree(src, src.getClass()));
|
||||
return wrapper;
|
||||
})
|
||||
.registerTypeAdapter(new TypeToken<HashMap<String, Serializable>>() {}.getType(),
|
||||
(JsonDeserializer<HashMap<String, Serializable>>) (json, typeOfT, context) ->
|
||||
{
|
||||
HashMap<String, Serializable> map = new HashMap<>();
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
|
||||
for(Map.Entry<String, JsonElement> entry : obj.entrySet())
|
||||
{
|
||||
String key = entry.getKey();
|
||||
JsonElement element = entry.getValue();
|
||||
|
||||
var option = NpcOption.getOption(key);
|
||||
if(option.isEmpty() || option.get().getType() == null)
|
||||
map.put(key, context.deserialize(element, Serializable.class));
|
||||
else
|
||||
map.put(key, context.deserialize(element, option.get().getType()));
|
||||
}
|
||||
return map;
|
||||
})
|
||||
.registerTypeAdapter(new TypeToken<Map<String, HashMap<String, Serializable>>>() {}.getType(),
|
||||
(JsonDeserializer<Map<String, HashMap<String, Serializable>>>) (json, typeOfT, context) ->
|
||||
{
|
||||
HashMap<String, HashMap<String, Serializable>> map = new HashMap<>();
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
|
||||
for(Map.Entry<String, JsonElement> entry : obj.entrySet())
|
||||
{
|
||||
UUID key;
|
||||
if(entry.getKey().equalsIgnoreCase("global"))
|
||||
key = new UUID(0L, 0L);
|
||||
else
|
||||
key = UUID.fromString(entry.getKey());
|
||||
|
||||
Type valType = new TypeToken<HashMap<String, Serializable>>() {}.getType();
|
||||
HashMap<String, Serializable> val = context.deserialize(entry.getValue(), valType);
|
||||
map.put(key.toString(), val);
|
||||
}
|
||||
return map;
|
||||
})
|
||||
.registerTypeAdapter(new TypeToken<Map<String, HashMap<String, Serializable>>>() {}.getType(),
|
||||
(JsonSerializer<Map<String, HashMap<String, Serializable>>>) (src, typeOfSrc, context) ->
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
UUID globalUuid = new UUID(0L, 0L);
|
||||
|
||||
src.forEach((key, value) ->
|
||||
{
|
||||
String keyStr = key.equals(globalUuid.toString()) ? "global" : key.toString();
|
||||
JsonElement valElement = context.serialize(value);
|
||||
obj.add(keyStr, valElement);
|
||||
});
|
||||
return obj;
|
||||
})
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
private final File file;
|
||||
|
||||
public ObjectSaver(@NotNull String file)
|
||||
{
|
||||
this(new File(file));
|
||||
}
|
||||
|
||||
public ObjectSaver(@NotNull File file)
|
||||
{
|
||||
if(file.getParentFile() != null)
|
||||
file.getParentFile().mkdirs();
|
||||
this.file = file;
|
||||
if(!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.createNewFile();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isJson()
|
||||
{
|
||||
if(!file.exists() || file.length() == 0)
|
||||
return true;
|
||||
try(BufferedReader reader = new BufferedReader(new FileReader(file)))
|
||||
{
|
||||
int firstChar = reader.read();
|
||||
while(firstChar != -1 && Character.isWhitespace(firstChar))
|
||||
{
|
||||
firstChar = reader.read();
|
||||
}
|
||||
return firstChar == '{' || firstChar == '[';
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void write(T object) throws IOException
|
||||
{
|
||||
this.write(object, false);
|
||||
}
|
||||
|
||||
public <T> void write(T object, boolean append) throws IOException
|
||||
{
|
||||
try(Writer writer = new OutputStreamWriter(new FileOutputStream(this.file, append), StandardCharsets.UTF_8))
|
||||
{
|
||||
GSON.toJson(object, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> @Nullable T read(Class<T> clazz)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!isJson())
|
||||
{
|
||||
try(FileInputStream fileIn = new FileInputStream(this.file);
|
||||
ObjectInputStream objectIn = new ObjectInputStream(fileIn))
|
||||
{
|
||||
return (T) objectIn.readObject();
|
||||
}
|
||||
}
|
||||
try(Reader reader = new InputStreamReader(new FileInputStream(this.file), StandardCharsets.UTF_8))
|
||||
{
|
||||
return GSON.fromJson(reader, clazz);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Serializable> @Nullable T read()
|
||||
{
|
||||
if(!isJson())
|
||||
{
|
||||
try(FileInputStream fileIn = new FileInputStream(this.file);
|
||||
ObjectInputStream objectIn = new ObjectInputStream(fileIn))
|
||||
{
|
||||
return (T) objectIn.readObject();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException("JSON reading requires specifying a target class type context via read(Class<T>).");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> @NotNull List<T> readList(Class<T[]> clazz)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!isJson())
|
||||
{
|
||||
try(FileInputStream fileIn = new FileInputStream(this.file);
|
||||
ObjectInputStream objectIn = new ObjectInputStream(fileIn))
|
||||
{
|
||||
return (List<T>) objectIn.readObject();
|
||||
}
|
||||
}
|
||||
try(Reader reader = new InputStreamReader(new FileInputStream(this.file), StandardCharsets.UTF_8))
|
||||
{
|
||||
T[] array = GSON.fromJson(reader, clazz);
|
||||
return array == null ? new ArrayList<>() : new ArrayList<>(List.of(array));
|
||||
}
|
||||
}
|
||||
catch(Exception var4)
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,96 +9,96 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.ChatFormatting")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.EnumChatFormat")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.ChatFormatting")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.EnumChatFormat")
|
||||
public enum ChatFormat implements Wrapper.EnumWrapper, Serializable
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BLACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BLACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
BLACK(ChatColor.BLACK),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_BLUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_BLUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
DARK_BLUE(ChatColor.DARK_BLUE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_GREEN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_GREEN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
DARK_GREEN(ChatColor.DARK_GREEN),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_AQUA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_AQUA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
DARK_AQUA(ChatColor.DARK_AQUA),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_RED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_RED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
DARK_RED(ChatColor.DARK_RED),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_PURPLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_PURPLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
DARK_PURPLE(ChatColor.DARK_PURPLE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "GOLD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "GOLD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "g")
|
||||
GOLD(ChatColor.GOLD),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "GRAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "GRAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "h")
|
||||
GRAY(ChatColor.GRAY),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DARK_GRAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DARK_GRAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "i")
|
||||
DARK_GRAY(ChatColor.DARK_GRAY),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BLUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BLUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "j")
|
||||
BLUE(ChatColor.BLUE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "GREEN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "GREEN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "k")
|
||||
GREEN(ChatColor.GREEN),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "AQUA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "l")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "AQUA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "l")
|
||||
AQUA(ChatColor.AQUA),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "RED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "RED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "m")
|
||||
RED(ChatColor.RED),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LIGHT_PURPLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LIGHT_PURPLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "n")
|
||||
LIGHT_PURPLE(ChatColor.LIGHT_PURPLE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "YELLOW")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "o")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "YELLOW")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "o")
|
||||
YELLOW(ChatColor.YELLOW),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "WHITE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "p")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "WHITE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "p")
|
||||
WHITE(ChatColor.WHITE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OBFUSCATED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OBFUSCATED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "q")
|
||||
OBFUSCATED(ChatColor.MAGIC),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BOLD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BOLD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "r")
|
||||
BOLD(ChatColor.BOLD),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "STRIKETHROUGH")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "s")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "STRIKETHROUGH")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "s")
|
||||
STRIKETHROUGH(ChatColor.STRIKETHROUGH),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UNDERLINE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "t")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UNDERLINE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "t")
|
||||
UNDERLINE(ChatColor.UNDERLINE),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ITALIC")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "u")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ITALIC")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "u")
|
||||
ITALIC(ChatColor.ITALIC),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "RESET")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "v")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "RESET")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "v")
|
||||
RESET(ChatColor.RESET);
|
||||
|
||||
private final ChatColor color;
|
||||
|
||||
@@ -7,31 +7,31 @@ import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedConnection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.ConnectionProtocol")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.EnumProtocol")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.ConnectionProtocol")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.EnumProtocol")
|
||||
public enum ConnectionProtocol implements Wrapper.EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "HANDSHAKE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "HANDSHAKE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
HANDSHAKE,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "PLAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "PLAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
PLAY,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "STATUS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "STATUS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
STATUS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LOGIN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LOGIN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
LOGIN,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CONFIGURATION")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CONFIGURATION")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
CONFIGURATION;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public @NotNull Object codecData(@NotNull WrappedConnection.WrappedNetworkManager.PacketFlow packetFlow)
|
||||
{
|
||||
return Reflections.invokeMethod(getHandle(), getPath(), packetFlow.getHandle()).get();
|
||||
|
||||
@@ -5,39 +5,39 @@ import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.EquipmentSlot")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EnumItemSlot")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.entity.EquipmentSlot")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EnumItemSlot")
|
||||
public enum EquipmentSlot implements Wrapper.EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "MAINHAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "MAINHAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
MAINHAND,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OFFHAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OFFHAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
OFFHAND,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "FEET")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "FEET")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
FEET,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LEGS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LEGS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
LEGS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CHEST")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CHEST")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
CHEST,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "HEAD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "HEAD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
HEAD,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BODY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BODY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "g")
|
||||
BODY,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SADDLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SADDLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "h")
|
||||
SADDLE;
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@ import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.InteractionHand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.EnumHand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.InteractionHand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.EnumHand")
|
||||
public enum InteractionHand implements Wrapper.EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "MAIN_HAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "MAIN_HAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
MAIN_HAND,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OFF_HAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OFF_HAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
OFF_HAND;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,84 +9,80 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.Pose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EntityPose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.entity.Pose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EntityPose")
|
||||
public enum Pose implements Wrapper.EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "STANDING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "STANDING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
STANDING(getBukkit("STANDING"), Material.ARMOR_STAND),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "FALL_FLYING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "FALL_FLYING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
FALL_FLYING(getBukkit("FALL_FLYING"), Material.FEATHER),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SLEEPING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SLEEPING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
SLEEPING(getBukkit("SLEEPING"), Material.RED_BED),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SWIMMING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SWIMMING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
SWIMMING(getBukkit("SWIMMING"), Material.WATER_BUCKET),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SPIN_ATTACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SPIN_ATTACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
SPIN_ATTACK(getBukkit("SPIN_ATTACK"), Material.TRIDENT),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CROUCHING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CROUCHING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
CROUCHING(getBukkit("SNEAKING"), Material.LEATHER_BOOTS),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LONG_JUMPING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LONG_JUMPING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "g")
|
||||
LONG_JUMPING(getBukkit("LONG_JUMPING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DYING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "h")
|
||||
DYING(getBukkit("DYING"), Versions.isCurrentVersionSmallerThan(Versions.V1_19_4) ? Material.ROTTEN_FLESH : null),
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DYING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "h")
|
||||
DYING(getBukkit("DYING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CROAKING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CROAKING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "i")
|
||||
CROAKING(getBukkit("CROAKING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "USING_TONGUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "USING_TONGUE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "j")
|
||||
USING_TONGUE(getBukkit("USING_TONGUE"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SITTING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "k")
|
||||
SITTING(getBukkit("SITTING"), Versions.isCurrentVersionSmallerThan(Versions.V1_19_4) ? null : Material.OAK_STAIRS),
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SITTING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "k")
|
||||
SITTING(getBukkit("SITTING"), Material.OAK_STAIRS),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ROARING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "l")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ROARING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "l")
|
||||
ROARING(getBukkit("ROARING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SNIFFING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "l")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SNIFFING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "m")
|
||||
SNIFFING(getBukkit("SNIFFING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "EMERGING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "EMERGING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "n")
|
||||
EMERGING(getBukkit("EMERGING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DIGGING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "o")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DIGGING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "o")
|
||||
DIGGING(getBukkit("DIGGING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SLIDING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_4, to = Versions.V1_21_11), path = "p")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SLIDING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "p")
|
||||
SLIDING(getBukkit("SLIDING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SHOOTING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_4, to = Versions.V1_21_11), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SHOOTING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "q")
|
||||
SHOOTING(getBukkit("SHOOTING"), null),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "INHALING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_4, to = Versions.V1_21_11), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "INHALING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "r")
|
||||
INHALING(getBukkit("INHALING"), null);
|
||||
|
||||
private final org.bukkit.entity.Pose bukkitPose;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.decoration.ArmorStand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.entity.decoration.EntityArmorStand")
|
||||
public class WrappedArmorStand extends WrappedEntity.WrappedNameTag<ArmorStand>
|
||||
{
|
||||
private WrappedArmorStand(Object handle)
|
||||
{
|
||||
super(handle);
|
||||
}
|
||||
|
||||
public static WrappedArmorStand create(World world)
|
||||
{
|
||||
return new WrappedArmorStand(
|
||||
createInstance(WrappedArmorStand.class, EntityTypes.ARMOR_STAND, Reflections.invokeMethod(world, "getHandle").get()));
|
||||
}
|
||||
|
||||
public WrappedEntityData applyData(WrappedComponent component)
|
||||
{
|
||||
WrappedEntityData data = getEntityData();
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BYTE.create(0), (byte) 0x20);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.OPTIONAL_CHAT_COMPONENT.create(2), Optional.of(component.getHandle()));
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(4), true);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BYTE.create(15), (byte) 0x10);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.ai.attributes.AttributeInstance")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.entity.ai.attributes.AttributeInstance")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.ai.attributes.AttributeModifiable")
|
||||
public class WrappedAttributeInstance extends Wrapper
|
||||
{
|
||||
@@ -20,18 +20,18 @@ public class WrappedAttributeInstance extends Wrapper
|
||||
super(createInstance(WrappedAttributeInstance.class, holder, consumer));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setBaseValue")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setBaseValue")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void setBaseValue(double value)
|
||||
{
|
||||
invokeWrappedMethod(value);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.ai.attributes.Attributes")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.entity.ai.attributes.Attributes")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.ai.attributes.GenericAttributes")
|
||||
public static class Attributes extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SCALE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SCALE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "A")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_9), path = "z")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21, to = Versions.V1_21_5), path = "y")
|
||||
|
||||
@@ -5,7 +5,7 @@ import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V26_1), path = "net.minecraft.server.level.ClientInformation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.server.level.ClientInformation")
|
||||
public class WrappedClientInformation extends Wrapper
|
||||
{
|
||||
private WrappedClientInformation(Object handle)
|
||||
@@ -13,8 +13,8 @@ public class WrappedClientInformation extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "createDefault")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "createDefault")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public static @NotNull WrappedClientInformation createDefault()
|
||||
{
|
||||
return new WrappedClientInformation(invokeStaticWrappedMethod());
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import de.eisi05.npc.api.NpcApi;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.Var;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.commands.CommandSourceStack")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.commands.CommandListenerWrapper")
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.commands.CommandSourceStack")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.commands.CommandListenerWrapper")
|
||||
public class WrappedCommandSourceStack extends Wrapper
|
||||
{
|
||||
public WrappedCommandSourceStack(Object handle)
|
||||
@@ -15,15 +23,23 @@ public class WrappedCommandSourceStack extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "getBukkitSender")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "getBukkitSender")
|
||||
public CommandSender getBukkitSender()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withMaximumPermission")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_9), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getEntity")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "f")
|
||||
public @NotNull WrappedEntity<?> getEntity()
|
||||
{
|
||||
return new WrappedEntity<>(invokeWrappedMethod());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withMaximumPermission")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withMaximumPermission()
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_21_11))
|
||||
@@ -31,21 +47,107 @@ public class WrappedCommandSourceStack extends Wrapper
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod(WrappedPermissionSet.ALL.getHandle()));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withSuppressedOutput")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withEntity(WrappedEntity<?> entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> wrapperClass = getWrappedClass(getClass());
|
||||
Method method = wrapperClass.getDeclaredMethod(getPath(), getWrappedClass(WrappedEntity.class));
|
||||
method.setAccessible(true);
|
||||
method.invoke(getHandle(), entity.getHandle());
|
||||
return new WrappedCommandSourceStack(method.invoke(getHandle(), entity.getHandle()));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(NpcApi.config.debug())
|
||||
e.printStackTrace();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withSuppressedOutput")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withSuppressedOutput()
|
||||
{
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V26_1), path = "net.minecraft.server.permissions.PermissionSet")
|
||||
public @NotNull WrappedCommandSourceStack withLocation(@NotNull Location location)
|
||||
{
|
||||
return withPosition(location.toVector()).withRotation(location.getYaw(), location.getPitch()).withWorld(location.getWorld());
|
||||
}
|
||||
|
||||
public @NotNull Location getLocation()
|
||||
{
|
||||
Vector position = getPosition();
|
||||
WrappedVec2F rotation = getRotation();
|
||||
return new Location(getWorld(), position.getX(), position.getY(), position.getZ(), rotation.getPitch(), rotation.getYaw());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withPosition")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withPosition(@NotNull Vector vector)
|
||||
{
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod(WrappedVec3D.fromVector(vector).getHandle()));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withRotation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withRotation(float yaw, float pitch)
|
||||
{
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod(new WrappedVec2F(yaw, pitch).getHandle()));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withLevel")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedCommandSourceStack withWorld(@NotNull World world)
|
||||
{
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod(Var.getNmsLevel(world)));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "isSilent")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "y")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_9), path = "x")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "y")
|
||||
public boolean isSilent()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getPosition")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "d")
|
||||
public @NotNull Vector getPosition()
|
||||
{
|
||||
return WrappedVec3D.fromHandle(invokeWrappedMethod()).toVector();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getRotation")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "l")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "k")
|
||||
public @NotNull WrappedVec2F getRotation()
|
||||
{
|
||||
return new WrappedVec2F(invokeWrappedMethod());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getLevel")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "e")
|
||||
public @NotNull World getWorld()
|
||||
{
|
||||
return (World) Reflections.invokeMethod(invokeWrappedMethod(), "getWorld").get();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V26_3), path = "net.minecraft.server.permissions.PermissionSet")
|
||||
private static class WrappedPermissionSet extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "NO_PERMISSIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "NO_PERMISSIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "g")
|
||||
public static final WrappedPermissionSet NO = new WrappedPermissionSet(getStaticWrappedFieldValue("NO").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ALL_PERMISSIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ALL_PERMISSIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "h")
|
||||
public static final WrappedPermissionSet ALL = new WrappedPermissionSet(getStaticWrappedFieldValue("ALL").orElse(null));
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ import java.util.function.UnaryOperator;
|
||||
* A wrapper class for Minecraft's chat component system, providing a type-safe way to create and manipulate
|
||||
* chat messages with formatting, click events, hover events, and other rich text features.
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.Component")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.IChatBaseComponent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.Component")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.IChatBaseComponent")
|
||||
public class WrappedComponent extends Wrapper
|
||||
{
|
||||
/**
|
||||
@@ -56,8 +56,8 @@ public class WrappedComponent extends Wrapper
|
||||
* @param text The text to display (can be null for empty component)
|
||||
* @return A new WrappedComponent containing the specified text
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "literal")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "literal")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public static @NotNull WrappedComponent create(@Nullable String text)
|
||||
{
|
||||
return new WrappedComponent(text == null ? CommonComponents.EMPTY : invokeStaticWrappedMethod(text));
|
||||
@@ -83,8 +83,8 @@ public class WrappedComponent extends Wrapper
|
||||
* @param format One or more ChatFormat values to apply (e.g., BOLD, ITALIC, UNDERLINE)
|
||||
* @return This component for method chaining
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedComponent setFormats(@NotNull ChatFormat... format)
|
||||
{
|
||||
if(format.length == 0)
|
||||
@@ -119,9 +119,8 @@ public class WrappedComponent extends Wrapper
|
||||
* @param clickEvent The click event to add
|
||||
* @return This component for method chaining
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "format")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedComponent withClickEvent(@NotNull ClickEvent clickEvent)
|
||||
{
|
||||
invokeWrappedMethod((UnaryOperator<Object>) o -> new Style(o).withClickEvent(clickEvent).getHandle());
|
||||
@@ -134,9 +133,8 @@ public class WrappedComponent extends Wrapper
|
||||
* @param hoverEvent The hover event to add
|
||||
* @return This component for method chaining
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "format")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withStyle")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull WrappedComponent withHoverEvent(@NotNull HoverEvent hoverEvent)
|
||||
{
|
||||
invokeWrappedMethod((UnaryOperator<Object>) o -> new Style(o).withHoverEvent(hoverEvent).getHandle());
|
||||
@@ -149,10 +147,8 @@ public class WrappedComponent extends Wrapper
|
||||
* @param wrappedComponent The component to append
|
||||
* @return This component for method chaining
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "append")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_1, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_19), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "addSibling")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "append")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public @NotNull WrappedComponent append(WrappedComponent wrappedComponent)
|
||||
{
|
||||
invokeWrappedMethod(wrappedComponent);
|
||||
@@ -164,10 +160,8 @@ public class WrappedComponent extends Wrapper
|
||||
*
|
||||
* @return A new WrappedComponent that is a copy of this one
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "copy")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_4, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_20_2), path = "e")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "mutableCopy")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "copy")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
public @NotNull WrappedComponent copy()
|
||||
{
|
||||
return new WrappedComponent(invokeWrappedMethod());
|
||||
@@ -205,13 +199,11 @@ public class WrappedComponent extends Wrapper
|
||||
return new SerializedComponent(toLegacy(true));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V26_1), path = "net.minecraft.network.chat.CommonComponents")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "net.minecraft.network.chat.ChatComponentText")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.network.chat.CommonComponents")
|
||||
private static class CommonComponents extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "EMPTY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "EMPTY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
private static final Object EMPTY = getStaticWrappedFieldValue("EMPTY").orElse(null);
|
||||
|
||||
private CommonComponents()
|
||||
@@ -220,8 +212,8 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable")
|
||||
public static class ClickEvent extends Wrapper
|
||||
{
|
||||
private ClickEvent(Object handle)
|
||||
@@ -236,41 +228,41 @@ public class WrappedComponent extends Wrapper
|
||||
return action.function.apply(value);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$Action")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$EnumClickAction")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$Action")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$EnumClickAction")
|
||||
public enum ClickAction implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPEN_URL")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPEN_URL")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
OPEN_URL(o -> new OpenUrl((URI) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPEN_FILE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPEN_FILE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
OPEN_FILE(o -> new OpenFile((String) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "RUN_COMMAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "RUN_COMMAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
RUN_COMMAND(o -> new RunCommand((String) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SUGGEST_COMMAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SUGGEST_COMMAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
SUGGEST_COMMAND(o -> new SuggestCommand((String) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SHOW_DIALOG")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SHOW_DIALOG")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "e")
|
||||
SHOW_DIALOG(ShowDialog::new),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CHANGE_PAGE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CHANGE_PAGE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_5), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_5), path = "e")
|
||||
CHANGE_PAGE(o -> new ChangePage((int) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "COPY_TO_CLIPBOARD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "COPY_TO_CLIPBOARD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_5), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_5), path = "f")
|
||||
COPY_TO_CLIPBOARD(o -> new CopyToClipboard((String) o)),
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CUSTOM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CUSTOM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "h")
|
||||
CUSTOM(o -> new Custom(((Object[]) o)[0], (Optional<?>) ((Object[]) o)[1]));
|
||||
|
||||
@@ -288,7 +280,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$OpenUrl")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$OpenUrl")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$OpenUrl")
|
||||
private static class OpenUrl extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -298,7 +290,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$OpenFile")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$OpenFile")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$OpenFile")
|
||||
private static class OpenFile extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -308,7 +300,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$RunCommand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$RunCommand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$RunCommand")
|
||||
private static class RunCommand extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -318,7 +310,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$SuggestCommand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$SuggestCommand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$SuggestCommand")
|
||||
private static class SuggestCommand extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -328,7 +320,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$ChangePage")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$ChangePage")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$ChangePage")
|
||||
private static class ChangePage extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -338,7 +330,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$CopyToClipboard")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$CopyToClipboard")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$CopyToClipboard")
|
||||
private static class CopyToClipboard extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -348,7 +340,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$ShowDialog")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$ShowDialog")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$h")
|
||||
private static class ShowDialog extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -358,7 +350,7 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.ClickEvent$Custom")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.ClickEvent$Custom")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatClickable$d")
|
||||
private static class Custom extends WrappedComponent.ClickEvent
|
||||
{
|
||||
@@ -369,8 +361,8 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.HoverEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatHoverable")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.HoverEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatHoverable")
|
||||
public static class HoverEvent extends Wrapper
|
||||
{
|
||||
private HoverEvent(Object handle)
|
||||
@@ -385,7 +377,7 @@ public class WrappedComponent extends Wrapper
|
||||
return new ShowText(component);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.HoverEvent$ShowText")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.HoverEvent$ShowText")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatHoverable$e")
|
||||
private static class ShowText extends HoverEvent
|
||||
{
|
||||
@@ -395,12 +387,12 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.HoverEvent$Action")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatHoverable$EnumHoverAction")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.HoverEvent$Action")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatHoverable$EnumHoverAction")
|
||||
private static class HoverAction extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SHOW_TEXT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SHOW_TEXT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
private static final Object SHOW_TEXT = getStaticWrappedFieldValue("SHOW_TEXT").orElse(null);
|
||||
|
||||
private HoverAction()
|
||||
@@ -428,8 +420,8 @@ public class WrappedComponent extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.chat.Style")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatModifier")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.chat.Style")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.chat.ChatModifier")
|
||||
public static class Style extends Wrapper
|
||||
{
|
||||
private Style(Object handle)
|
||||
@@ -437,17 +429,15 @@ public class WrappedComponent extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withClickEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setChatClickable")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withClickEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull Style withClickEvent(@NotNull ClickEvent clickEvent)
|
||||
{
|
||||
return new Style(invokeWrappedMethod(clickEvent));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "withHoverEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setChatHoverable")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "withHoverEvent")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @NotNull Style withHoverEvent(@NotNull HoverEvent hoverEvent)
|
||||
{
|
||||
return new Style(invokeWrappedMethod(hoverEvent));
|
||||
|
||||
@@ -4,17 +4,13 @@ import com.mojang.authlib.GameProfile;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import de.eisi05.npc.api.wrapper.enums.ConnectionProtocol;
|
||||
import de.eisi05.npc.api.wrapper.packets.PacketWrapper;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.server.network.ServerGamePacketListenerImpl")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "net.minecraft.server.network.PlayerConnection")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "net.minecraft.network.PlayerConnection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.server.network.ServerGamePacketListenerImpl")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.server.network.PlayerConnection")
|
||||
public class WrappedConnection extends Wrapper
|
||||
{
|
||||
WrappedConnection(Object handle)
|
||||
@@ -28,28 +24,23 @@ public class WrappedConnection extends Wrapper
|
||||
super(createInstance(WrappedConnection.class, server, networkManager, player, commonListenerCookie));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "sendPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_20), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "sendPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "sendPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public void sendPacket(@NotNull PacketWrapper packet)
|
||||
{
|
||||
invokeWrappedMethod(packet);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_20_4), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_3), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "c")
|
||||
public @NotNull WrappedNetworkManager networkManager()
|
||||
{
|
||||
return new WrappedNetworkManager(getWrappedFieldValue());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.Connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.NetworkManager")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.Connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.NetworkManager")
|
||||
public static class WrappedNetworkManager extends Wrapper
|
||||
{
|
||||
private WrappedNetworkManager(Object handle)
|
||||
@@ -59,50 +50,35 @@ public class WrappedConnection extends Wrapper
|
||||
|
||||
public static @NotNull WrappedNetworkManager create(@NotNull PacketFlow packetFlow)
|
||||
{
|
||||
WrappedNetworkManager networkManager = new WrappedNetworkManager(createInstance(WrappedNetworkManager.class, packetFlow));
|
||||
|
||||
if(Versions.getVersion() != Versions.V1_20_2 && Versions.getVersion() != Versions.V1_20_4)
|
||||
return networkManager;
|
||||
|
||||
NioSocketChannel socketChannel = new NioSocketChannel();
|
||||
socketChannel.attr(AttributeKey.valueOf("serverbound_protocol")).set(ConnectionProtocol.PLAY.codecData(PacketFlow.SERVERBOUND));
|
||||
socketChannel.attr(AttributeKey.valueOf("clientbound_protocol")).set(ConnectionProtocol.PLAY.codecData(PacketFlow.SERVERBOUND));
|
||||
|
||||
networkManager.setChannel(socketChannel);
|
||||
|
||||
return networkManager;
|
||||
return new WrappedNetworkManager(createInstance(WrappedNetworkManager.class, packetFlow));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "channel")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "channel")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_9), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18_2, to = Versions.V1_20), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "n")
|
||||
public @NotNull Channel channel()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "channel")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "channel")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V1_21_11), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_9), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18_2, to = Versions.V1_20), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "n")
|
||||
public void setChannel(@Nullable Channel channel)
|
||||
{
|
||||
setWrappedFieldValue(channel);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.PacketFlow")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.EnumProtocolDirection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.PacketFlow")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.EnumProtocolDirection")
|
||||
public enum PacketFlow implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "SERVERBOUND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "SERVERBOUND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
SERVERBOUND,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "CLIENTBOUND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "CLIENTBOUND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
CLIENTBOUND;
|
||||
|
||||
@Override
|
||||
@@ -113,7 +89,7 @@ public class WrappedConnection extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V26_1), path = "net.minecraft.server.network.CommonListenerCookie")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.server.network.CommonListenerCookie")
|
||||
public static class CommonListenerCookie extends Wrapper
|
||||
{
|
||||
public CommonListenerCookie(@NotNull GameProfile profile, int latency, boolean transferred)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import org.bukkit.entity.EnderDragon;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.entity.boss.enderdragon.EnderDragon")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.boss.EntityEnderDragon")
|
||||
public class WrappedEnderDragon extends WrappedEntity<EnderDragon>
|
||||
{
|
||||
WrappedEnderDragon(Object handle)
|
||||
{
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "subEntities")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "cF")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "cD")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "cu")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_5), path = "bS")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_4), path = "cj")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_2), path = "ck")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21), path = "co")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "ck")
|
||||
public List<Integer> getSubIds()
|
||||
{
|
||||
Object[] subParts = getWrappedFieldValue();
|
||||
return Arrays.stream(subParts).map(object -> new WrappedEntity<>(object).getId()).toList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import de.eisi05.npc.api.objects.NameDisplayOptions;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
@@ -16,7 +17,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.entity.Entity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.world.entity.Entity")
|
||||
public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
{
|
||||
public String data = "{}";
|
||||
@@ -57,21 +58,20 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
return false;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "getBukkitEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "getBukkitEntity")
|
||||
public @NotNull T getBukkitPlayer()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "absSnapTo")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setLocation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "absSnapTo")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void moveTo(@NotNull Location location)
|
||||
{
|
||||
invokeWrappedMethod(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getEntityData")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getEntityData")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "aD")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "aC")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "au")
|
||||
@@ -79,60 +79,36 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "au")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21), path = "ar")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "ap")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "an")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "al")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20), path = "aj")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "al")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_19_1), path = "ai")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getDataWatcher")
|
||||
public @NotNull WrappedEntityData getEntityData()
|
||||
{
|
||||
return new WrappedEntityData(invokeWrappedMethod());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "passengers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "passengers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "aS")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "aR")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_5), path = "u")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "p")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_19_3), path = "au")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "at")
|
||||
public void setPassengers(WrappedEntity<?>... entities)
|
||||
{
|
||||
setWrappedFieldValue(ImmutableList.copyOf(Arrays.stream(entities).map(wrappedEntity -> wrappedEntity.handle).toList()));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "level")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "level")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "ao")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "an")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "ai")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "cU")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "cN")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "cK")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "cJ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "cH")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_4), path = "cG")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "cH")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_1), path = "cC")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19), path = "cD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_18_2), path = "cA")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getWorld")
|
||||
public @NotNull Object getServer()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getAddEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getAddEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "dl")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "dj")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "di")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20), path = "S")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "T")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_19_1), path = "S")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getPacket")
|
||||
public @NotNull PacketWrapper getAddEntityPacket()
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_21))
|
||||
@@ -140,19 +116,12 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
return new CustomPacket(invokeWrappedMethod(new WrappedEntityTrackerEntry(this)));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getBoundingBox")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getBoundingBox")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "dj")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "de")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "cV")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "cR")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "cK")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "cH")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "cG")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "cE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_19_4), path = "cD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "cz")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_18_2), path = "cw")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getBoundingBox")
|
||||
public BoundingBox getBoundingBox()
|
||||
{
|
||||
return new BoundingBox(invokeWrappedMethod());
|
||||
@@ -163,7 +132,7 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
return defaultBoundingBox;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setId")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setId")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "e")
|
||||
public void setId(int id)
|
||||
{
|
||||
@@ -180,11 +149,11 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
return getBukkitPlayer().getWorld();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V26_1), path = "net.minecraft.world.entity.Entity$RemovalReason")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.world.entity.Entity$RemovalReason")
|
||||
enum RemovalReason implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DISCARDED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DISCARDED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
DISCARDED;
|
||||
|
||||
@Override
|
||||
@@ -194,10 +163,10 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V26_1), path = "net.minecraft.world.entity.EntitySpawnReason")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V26_3), path = "net.minecraft.world.entity.EntitySpawnReason")
|
||||
enum SpawnReason implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LOAD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LOAD")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_11), path = "r")
|
||||
LOAD;
|
||||
|
||||
@@ -208,10 +177,10 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V26_1), path = "net.minecraft.world.entity.EntityProcessor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_11, to = Versions.V26_3), path = "net.minecraft.world.entity.EntityProcessor")
|
||||
static class EntityProcessor extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "NOP")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "NOP")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "a")
|
||||
public static final Object NOP = getStaticWrappedFieldValue("NOP").orElse(null);
|
||||
|
||||
@@ -221,19 +190,19 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.entity.EntityType")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EntityTypes")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_2, to = Versions.V26_3), path = "net.minecraft.world.entity.EntityTypes")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V26_1), path = "net.minecraft.world.entity.EntityType")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.entity.EntityTypes")
|
||||
public static class EntityTypes extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ARMOR_STAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ARMOR_STAND")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_6), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "d")
|
||||
public static final Object ARMOR_STAND = getStaticWrappedFieldValue("ARMOR_STAND").orElse(null);
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "TEXT_DISPLAY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "TEXT_DISPLAY")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "bD")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "bA")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "bx")
|
||||
@@ -241,18 +210,26 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_4), path = "bu")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_2), path = "bv")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "bb")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "aY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_2), path = "aX")
|
||||
public static final Object TEXT_DISPLAY = getStaticWrappedFieldValue("TEXT_DISPLAY").orElse(null);
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "INTERACTION")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "at")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "as")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "aq")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_5), path = "ap")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_4), path = "ao")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_2), path = "ap")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "ae")
|
||||
public static final Object INTERACTION = getStaticWrappedFieldValue("INTERACTION").orElse(null);
|
||||
|
||||
private EntityTypes()
|
||||
{
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.phys.AABB")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.phys.AxisAlignedBB")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.phys.AABB")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.phys.AxisAlignedBB")
|
||||
public static class BoundingBox extends Wrapper
|
||||
{
|
||||
private BoundingBox(Object handle)
|
||||
@@ -260,22 +237,22 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getXsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getXsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public double getXSize()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getYsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getYsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
public double getYSize()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getZsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getZsize")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
public double getZSize()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
@@ -289,6 +266,6 @@ public class WrappedEntity<T extends Entity> extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
public abstract WrappedEntityData applyData(WrappedComponent component);
|
||||
public abstract WrappedEntityData applyData(WrappedComponent component, @NotNull NameDisplayOptions displayOptions);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.syncher.SynchedEntityData")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcher")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.syncher.SynchedEntityData")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcher")
|
||||
public class WrappedEntityData extends Wrapper
|
||||
{
|
||||
WrappedEntityData(Object handle)
|
||||
@@ -22,47 +22,43 @@ public class WrappedEntityData extends Wrapper
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "set")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "set")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_20_4), path = "b")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "set")
|
||||
public <T> void set(@NotNull EntityDataAccessor<T> accessor, @Nullable T value)
|
||||
{
|
||||
invokeWrappedMethod(accessor, value);
|
||||
markDirty(accessor);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "get")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "get")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "get")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public <T> @Nullable T get(@NotNull EntityDataAccessor<T> accessor)
|
||||
{
|
||||
return invokeWrappedMethod(accessor);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "packDirty")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "packDirty")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public @NotNull List<?> packDirty()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "markDirty")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "markDirty")
|
||||
public <T> void markDirty(@NotNull EntityDataAccessor<T> accessor)
|
||||
{
|
||||
invokeWrappedMethod(accessor);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getNonDefaultValues")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getAll")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getNonDefaultValues")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
public List<?> getAll()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.syncher.EntityDataAccessor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherObject")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.syncher.EntityDataAccessor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherObject")
|
||||
public static class EntityDataAccessor<T> extends Wrapper
|
||||
{
|
||||
private EntityDataAccessor(Object handle)
|
||||
@@ -76,8 +72,8 @@ public class WrappedEntityData extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.syncher.EntityDataSerializer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherSerializer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.syncher.EntityDataSerializer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherSerializer")
|
||||
public static class EntityDataSerializer<T> extends Wrapper
|
||||
{
|
||||
private EntityDataSerializer(Object handle)
|
||||
@@ -91,196 +87,153 @@ public class WrappedEntityData extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.syncher.EntityDataSerializers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherRegistry")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.syncher.EntityDataSerializers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.syncher.DataWatcherRegistry")
|
||||
public static class EntityDataSerializers extends Wrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BYTE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BYTE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public static final EntityDataSerializer<Byte> BYTE = new EntityDataSerializer<>(getStaticWrappedFieldValue("BYTE").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "INT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "INT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public static final EntityDataSerializer<Integer> INT = new EntityDataSerializer<>(getStaticWrappedFieldValue("INT").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "LONG")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "LONG")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
public static final EntityDataSerializer<Long> LONG = new EntityDataSerializer<>(getStaticWrappedFieldValue("LONG").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "FLOAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "FLOAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
public static final EntityDataSerializer<Float> FLOAT = new EntityDataSerializer<>(getStaticWrappedFieldValue("FLOAT").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "STRING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "STRING")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
public static final EntityDataSerializer<String> STRING = new EntityDataSerializer<>(getStaticWrappedFieldValue("STRING").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "COMPONENT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "COMPONENT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
public static final EntityDataSerializer<?> CHAT_COMPONENT = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("CHAT_COMPONENT").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_COMPONENT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_COMPONENT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "g")
|
||||
public static final EntityDataSerializer<Optional<?>> OPTIONAL_CHAT_COMPONENT = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_CHAT_COMPONENT").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ITEM_STACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ITEM_STACK")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "h")
|
||||
public static final EntityDataSerializer<?> ITEM_STACK = new EntityDataSerializer<>(getStaticWrappedFieldValue("ITEM_STACK").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BLOCK_STATE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_21_11), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BLOCK_STATE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "i")
|
||||
public static final EntityDataSerializer<?> BLOCK_DATA = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("BLOCK_DATA").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_BLOCK_STATE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_21_11), path = "j")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_BLOCK_STATE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "j")
|
||||
public static final EntityDataSerializer<Optional<?>> OPTIONAL_BLOCK_DATA = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_BLOCK_DATA").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BOOLEAN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_21_11), path = "k")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BOOLEAN")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "k")
|
||||
public static final EntityDataSerializer<Boolean> BOOLEAN = new EntityDataSerializer<>(getStaticWrappedFieldValue("BOOLEAN").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "PARTICLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_21_11), path = "l")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "PARTICLE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "l")
|
||||
public static final EntityDataSerializer<?> PARTICLE_PARAM = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("PARTICLE_PARAM").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "PARTICLES")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "PARTICLES")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "m")
|
||||
public static final EntityDataSerializer<List<?>> PARTICLE_PARAM_LIST = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("PARTICLE_PARAM_LIST").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ROTATIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ROTATIONS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "m")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "l")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "k")
|
||||
public static final EntityDataSerializer<?> VECTOR = new EntityDataSerializer<>(getStaticWrappedFieldValue("VECTOR").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "BLOCK_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "BLOCK_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "o")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "n")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "m")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "l")
|
||||
public static final EntityDataSerializer<?> BLOCK_POSITION = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("BLOCK_POSITION").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_BLOCK_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_BLOCK_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "p")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "o")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "n")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "m")
|
||||
public static final EntityDataSerializer<?> OPTIONAL_BLOCK_POSITION = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_BLOCK_POSITION").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "DIRECTION")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "DIRECTION")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "p")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "o")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "n")
|
||||
public static final EntityDataSerializer<?> ENUM_DIRECTION = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("ENUM_DIRECTION").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "q")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "p")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "o")
|
||||
public static final EntityDataSerializer<Optional<UUID>> OPTIONAL_UUID = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_UUID").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_LIVING_ENTITY_REFERENCE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_LIVING_ENTITY_REFERENCE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "r")
|
||||
public static final EntityDataSerializer<Optional<?>> OPTIONAL_ENTITY_REFERENCE = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_ENTITY_REFERENCE").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_GLOBAL_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_GLOBAL_POS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "s")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "r")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "p")
|
||||
public static final EntityDataSerializer<Optional<?>> OPTIONAL_GLOBAL_POS = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_GLOBAL_POS").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_6), path = "t")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "s")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "q")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "p")
|
||||
public static final EntityDataSerializer<?> NBT_TAG = new EntityDataSerializer<>(getStaticWrappedFieldValue("NBT_TAG").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "VILLAGER_DATA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "VILLAGER_DATA")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "t")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_6), path = "u")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "t")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "s")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "r")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "q")
|
||||
public static final EntityDataSerializer<?> VILLAGER_DATA = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("VILLAGER_DATA").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "OPTIONAL_UNSIGNED_INT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "OPTIONAL_UNSIGNED_INT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "u")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_6), path = "v")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "u")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "t")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "s")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "r")
|
||||
public static final EntityDataSerializer<OptionalInt> OPTIONAL_INT = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("OPTIONAL_INT").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "POSE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "POSE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "v")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_6), path = "w")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "v")
|
||||
@Mapping(fixed = @Mapping.Fixed(value = Versions.V1_19_3), path = "u")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_19_1), path = "t")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "s")
|
||||
public static final EntityDataSerializer<Pose> ENTITY_POSE = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("ENTITY_POSE").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "VECTOR3")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "VECTOR3")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "J")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "I")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_6), path = "H")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "D")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "A")
|
||||
public static final EntityDataSerializer<Vector3f> VECTOR3 = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("VECTOR3").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "QUARTERNION")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "QUARTERNION")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "K")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "J")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_6), path = "I")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "E")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20_4), path = "B")
|
||||
public static final EntityDataSerializer<?> QUARTERNION = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("QUARTERNION").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "RESOLVABLE_PROFILE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "RESOLVABLE_PROFILE")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "L")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "K")
|
||||
public static final EntityDataSerializer<?> RESOLVABLE_PROFILE = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("RESOLVABLE_PROFILE").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "HUMANOID_ARM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "HUMANOID_ARM")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "M")
|
||||
public static final EntityDataSerializer<?> HAND = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("HAND").orElse(null));
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "DYE_COLOR")
|
||||
public static final EntityDataSerializer<?> DYE_COLOR = new EntityDataSerializer<>(
|
||||
getStaticWrappedFieldValue("DYE_COLOR").orElse(null));
|
||||
|
||||
private EntityDataSerializers()
|
||||
{
|
||||
super(null);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.SerializableFunction;
|
||||
@@ -18,6 +20,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
@@ -30,8 +33,10 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String type;
|
||||
|
||||
@JsonAdapter(SnapshotDataJsonAdapter.class)
|
||||
private final byte[] data;
|
||||
private final SerializableFunction<? extends Entity, ? extends Entity> entityFunction;
|
||||
private transient final SerializableFunction<? extends Entity, ? extends Entity> entityFunction;
|
||||
|
||||
/**
|
||||
* Creates a new entity snapshot with the specified entity type and NBT data.
|
||||
@@ -89,7 +94,6 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
return data == null ? new WrappedCompoundTag() : WrappedCompoundTag.parse(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new entity in the specified world using this snapshot's data.
|
||||
*
|
||||
@@ -100,39 +104,35 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NotNull WrappedEntity<?> create(@NotNull World world)
|
||||
{
|
||||
WrappedEntity<?> entity;
|
||||
Object handle;
|
||||
WrappedCompoundTag data = getData();
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_21_2))
|
||||
{
|
||||
data.putString("id", type.toLowerCase());
|
||||
|
||||
entity = new WrappedEntity<>(Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a",
|
||||
data.getHandle(), Var.getNmsLevel(world), Function.identity()).get());
|
||||
handle = Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a",
|
||||
data.getHandle(), Var.getNmsLevel(world), Function.identity()).get();
|
||||
}
|
||||
else if(Versions.isCurrentVersionSmallerThan(Versions.V1_21_9))
|
||||
{
|
||||
data.putString("id", type.toLowerCase());
|
||||
entity = new WrappedEntity<>(
|
||||
Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a",
|
||||
data.getHandle(), Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(), Function.identity()).get());
|
||||
handle = Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a",
|
||||
data.getHandle(), Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(), Function.identity()).get();
|
||||
}
|
||||
else if(Versions.isCurrentVersionSmallerThan(Versions.V1_21_11))
|
||||
entity = new WrappedEntity<>(
|
||||
Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a", Var.toNmsEntityType(getType()), data.getHandle(),
|
||||
Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(), Function.identity()).get());
|
||||
handle = Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a", Var.toNmsEntityType(getType()), data.getHandle(),
|
||||
Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(), Function.identity()).get();
|
||||
else if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
entity = new WrappedEntity<>(
|
||||
Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a", Var.toNmsEntityType(getType()),
|
||||
handle = Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityTypes", "a", Var.toNmsEntityType(getType()),
|
||||
data.getHandle(), Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(),
|
||||
WrappedEntity.EntityProcessor.NOP).get());
|
||||
WrappedEntity.EntityProcessor.NOP).get();
|
||||
else
|
||||
entity = new WrappedEntity<>(
|
||||
Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityType", "loadEntityRecursive",
|
||||
handle = Reflections.invokeStaticMethod("net.minecraft.world.entity.EntityType", "loadEntityRecursive",
|
||||
Var.toNmsEntityType(getType()), data.getHandle(), Var.getNmsLevel(world), WrappedEntity.SpawnReason.LOAD.getHandle(),
|
||||
WrappedEntity.EntityProcessor.NOP).get());
|
||||
WrappedEntity.EntityProcessor.NOP).get();
|
||||
|
||||
WrappedEntity<?> entity = getType() == EntityType.ENDER_DRAGON ? new WrappedEnderDragon(handle) : new WrappedEntity<>(handle);
|
||||
WrappedEntity<?> finalEntity = entityFunction == null ? entity :
|
||||
WrappedEntity.fromEntity(entityFunction.apply(Var.unsafeCast(entity.getBukkitPlayer())), WrappedEntity.class);
|
||||
WrappedEntity.fromEntity(entityFunction.apply(Var.unsafeCast(entity.getBukkitPlayer())), entity.getClass());
|
||||
finalEntity.data = data.toString();
|
||||
return finalEntity;
|
||||
}
|
||||
@@ -141,8 +141,8 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
/**
|
||||
* A wrapper for Minecraft's NBT (Named Binary Tag) compound tag system, providing a type-safe way to read and write NBT data.
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.nbt.CompoundTag")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.nbt.NBTTagCompound")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.nbt.CompoundTag")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.nbt.NBTTagCompound")
|
||||
public static class WrappedCompoundTag extends Wrapper
|
||||
{
|
||||
/**
|
||||
@@ -177,8 +177,7 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return new WrappedCompoundTag(Reflections.invokeStaticMethod("net.minecraft.nbt.TagParser", "parseCompoundFully", s).get());
|
||||
|
||||
String methodName = Versions.isCurrentVersionSmallerThan(Versions.V1_18) ? "parse" : "a";
|
||||
return new WrappedCompoundTag(Reflections.invokeStaticMethod("net.minecraft.nbt.MojangsonParser", methodName, s).get());
|
||||
return new WrappedCompoundTag(Reflections.invokeStaticMethod("net.minecraft.nbt.MojangsonParser", "a", s).get());
|
||||
}
|
||||
catch(RuntimeException e1)
|
||||
{
|
||||
@@ -196,10 +195,6 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
*/
|
||||
public static WrappedCompoundTag parse(byte[] bytes)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_20_4))
|
||||
return new WrappedCompoundTag(
|
||||
Reflections.invokeStaticMethod("net.minecraft.nbt.NBTCompressedStreamTools", "a", new ByteArrayInputStream(bytes)).get());
|
||||
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_1))
|
||||
return new WrappedCompoundTag(
|
||||
Reflections.invokeStaticMethod("net.minecraft.nbt.NBTCompressedStreamTools", "a", new ByteArrayInputStream(bytes),
|
||||
@@ -216,9 +211,8 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
* @param key The key to store the value under
|
||||
* @param value The string value to store
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "putString")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setString")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "putString")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void putString(@NotNull String key, @NotNull String value)
|
||||
{
|
||||
invokeWrappedMethod(key, value);
|
||||
@@ -230,10 +224,9 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
* @param key The key of the boolean value
|
||||
* @return The boolean value, or false if the key doesn't exist
|
||||
*/
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getBooleanOr")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getBooleanOr")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_4), path = "q")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getBoolean")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "q")
|
||||
public boolean getBoolean(@NotNull String key)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_21_5))
|
||||
@@ -258,8 +251,8 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.nbt.NbtAccounter")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.nbt.NBTReadLimiter")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.nbt.NbtAccounter")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.nbt.NBTReadLimiter")
|
||||
public static class NbtAccounter extends Wrapper
|
||||
{
|
||||
private NbtAccounter(Object handle)
|
||||
@@ -267,12 +260,48 @@ public class WrappedEntitySnapshot implements Serializable
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "unlimitedHeap")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "unlimitedHeap")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_2, to = Versions.V1_21_9), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "a")
|
||||
public static NbtAccounter unlimitedHeap()
|
||||
{
|
||||
return new NbtAccounter(invokeStaticWrappedMethod());
|
||||
}
|
||||
}
|
||||
|
||||
private static class SnapshotDataJsonAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if (src == null || src.length == 0)
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
return new JsonPrimitive(WrappedCompoundTag.parse(src).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if (json == null || json.isJsonNull())
|
||||
return null;
|
||||
|
||||
if (json.isJsonPrimitive())
|
||||
{
|
||||
try
|
||||
{
|
||||
return WrappedCompoundTag.parse(json.getAsString()).getData();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new JsonParseException("Failed to parse SNBT string back to NBT byte array", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (json.isJsonArray())
|
||||
return context.deserialize(json, byte[].class);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.server.level.ServerEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.server.level.ServerEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21, to = Versions.V1_21_11), path = "net.minecraft.server.level.EntityTrackerEntry")
|
||||
public class WrappedEntityTrackerEntry extends Wrapper
|
||||
{
|
||||
@@ -32,7 +32,16 @@ public class WrappedEntityTrackerEntry extends Wrapper
|
||||
}, (BiConsumer<Object, List<UUID>>) (o, uuids) ->
|
||||
{
|
||||
}, Set.of());
|
||||
else
|
||||
else if(Versions.isCurrentVersionSmallerThan(Versions.V26_3))
|
||||
return createInstance(WrappedEntityTrackerEntry.class, entity.getServer(), entity, 0, false, null, Set.of());
|
||||
else
|
||||
return createInstance(WrappedEntityTrackerEntry.class, entity.getServer(), entity, UpdateInterval.NEVER, false, null, Set.of());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "net.minecraft.world.entity.UpdateInterval")
|
||||
private static class UpdateInterval
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "NEVER")
|
||||
private static final Object NEVER = getStaticWrappedFieldValue("NEVER").orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Interaction;
|
||||
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.world.entity.Interaction")
|
||||
public class WrappedInteraction extends WrappedEntity<Interaction>
|
||||
{
|
||||
WrappedInteraction(Object handle)
|
||||
{
|
||||
super(handle);
|
||||
}
|
||||
|
||||
public static WrappedInteraction create(World world)
|
||||
{
|
||||
return new WrappedInteraction(
|
||||
createInstance(WrappedInteraction.class, EntityTypes.INTERACTION, Reflections.invokeMethod(world, "getHandle").get()));
|
||||
}
|
||||
|
||||
public WrappedEntityData getData(float width, float height)
|
||||
{
|
||||
WrappedEntityData data = getEntityData();
|
||||
data.set(WrappedEntityData.EntityDataSerializers.FLOAT.create(8), width);
|
||||
data.set(WrappedEntityData.EntityDataSerializers.FLOAT.create(9), height);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "net.minecraft.server.MinecraftServer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.server.MinecraftServer")
|
||||
public class WrappedMinecraftServer extends Wrapper
|
||||
{
|
||||
public static final WrappedMinecraftServer INSTANCE = new WrappedMinecraftServer(Reflections.invokeMethod(Bukkit.getServer(), "getServer").get());
|
||||
@@ -23,32 +23,33 @@ public class WrappedMinecraftServer extends Wrapper
|
||||
return Reflections.getStaticField(INSTANCE.handle.getClass(), "currentTick");
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "vanillaCommandDispatcher")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "vanillaCommandDispatcher")
|
||||
public @NotNull WrappedMinecraftServer commands()
|
||||
{
|
||||
return new WrappedMinecraftServer(getWrappedFieldValue());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "registryAccess")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "registryAccess")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "bc")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "bg")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_6), path = "ba")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "bc")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "aZ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "aU")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "aV")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_4), path = "aX")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "aW")
|
||||
public Object registryAccess()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getDispatcher")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getDispatcher")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NotNull CommandDispatcher<Object> getCommandDispatcher()
|
||||
{
|
||||
return (CommandDispatcher<Object>) Reflections.invokeMethod(commands().getHandle(), getPath()).get();
|
||||
}
|
||||
|
||||
public static Object vanillaRegistry() {
|
||||
return Reflections.invokeStaticMethod("net.minecraft.data.registries.VanillaRegistries",
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V26_1) ? "a" :
|
||||
Versions.isCurrentVersionSmallerThan(Versions.V26_3) ? "createLookup" : "createWorldLookup").get();
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,10 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.scores.PlayerTeam")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.scores.ScoreboardTeam")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.scores.PlayerTeam")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.scores.ScoreboardTeam")
|
||||
public class WrappedPlayerTeam extends Wrapper
|
||||
{
|
||||
private static final Map<UUID, Map<String, WrappedPlayerTeam>> teams = new HashMap<>();
|
||||
@@ -30,12 +27,9 @@ public class WrappedPlayerTeam extends Wrapper
|
||||
return new WrappedPlayerTeam(Reflections.invokeMethod(player.getScoreboard(), "getHandle")
|
||||
.thanInvoke(switch(Versions.getVersion())
|
||||
{
|
||||
case NONE -> null;
|
||||
case V1_17 -> "getPlayerTeam";
|
||||
case V1_18, V1_18_2, V1_19, V1_19_1, V1_19_3, V1_19_4, V1_20 -> "i";
|
||||
case V1_20_2 -> "g";
|
||||
case V1_20_4, V1_20_6, V1_21, V1_21_2, V1_21_4, V1_21_5, V1_21_6, V1_21_9, V1_21_11 -> "e";
|
||||
case V26_1 -> "getPlayersTeam";
|
||||
case V1_20_6, V1_21, V1_21_2, V1_21_4, V1_21_5, V1_21_6, V1_21_9, V1_21_11 -> "e";
|
||||
case V26_1, V26_2, V26_3 -> "getPlayersTeam";
|
||||
default -> null;
|
||||
}, WrappedServerPlayer.fromPlayer(player).getName()).get());
|
||||
}
|
||||
|
||||
@@ -81,10 +75,9 @@ public class WrappedPlayerTeam extends Wrapper
|
||||
return teams.getOrDefault(player.getUniqueId(), new HashMap<>()).containsKey(name);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getNameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "nameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_4), path = "j")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getNameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "j")
|
||||
public @Nullable Visibility getNameTagVisibility()
|
||||
{
|
||||
Object enumHandle = getWrappedFieldValue();
|
||||
@@ -96,92 +89,90 @@ public class WrappedPlayerTeam extends Wrapper
|
||||
return null;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setNameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setNameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setNameTagVisibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void setNameTagVisibility(@NotNull Visibility visibility)
|
||||
{
|
||||
invokeWrappedMethod(visibility.getHandle());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setCollisionRule")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setCollisionRule")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setCollisionRule")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void setCollisionRule(@NotNull CollisionRule collisionRule)
|
||||
{
|
||||
invokeWrappedMethod(collisionRule.getHandle());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setCanSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public void setCanSeeFriendlyInvisible(boolean canSeeFriendlyInvisible)
|
||||
{
|
||||
invokeWrappedMethod(canSeeFriendlyInvisible);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "canSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "canSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_4), path = "i")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "canSeeFriendlyInvisibles")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "i")
|
||||
public boolean canSeeFriendlyInvisible()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getPlayers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getPlayers")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_4), path = "g")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getPlayerNameSet")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "g")
|
||||
public @NotNull Collection<String> getPlayers()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getName")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getName")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_5, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_4), path = "b")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getName")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_4), path = "b")
|
||||
public @NotNull String getName()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setColor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setColor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setColor")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void setColor(@NotNull ChatFormat color)
|
||||
{
|
||||
invokeWrappedMethod(color);
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_2))
|
||||
invokeWrappedMethod(color);
|
||||
else
|
||||
{
|
||||
Object teamColor = Reflections.getStaticField(getWrappedClass(TeamColor.class), color.name());
|
||||
invokeWrappedMethod(Optional.of(teamColor));
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setPlayerPrefix")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setPrefix")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setPlayerPrefix")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void setPrefix(@NotNull WrappedComponent prefix)
|
||||
{
|
||||
invokeWrappedMethod(prefix);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.scores.Team$Visibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.scores" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.scores.Team$Visibility")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.scores" +
|
||||
".ScoreboardTeamBase$EnumNameTagVisibility")
|
||||
public enum Visibility implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ALWAYS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ALWAYS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
ALWAYS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "NEVER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "NEVER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
NEVER,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "HIDE_FOR_OTHER_TEAMS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "HIDE_FOR_OTHER_TEAMS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
HIDE_FOR_OTHER_TEAMS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "HIDE_FOR_OWN_TEAM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "HIDE_FOR_OWN_TEAM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
HIDE_FOR_OWN_TEAM;
|
||||
|
||||
@Override
|
||||
@@ -191,24 +182,24 @@ public class WrappedPlayerTeam extends Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.scores.Team$CollisionRule")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.scores.Team$CollisionRule")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush")
|
||||
public enum CollisionRule implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ALWAYS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ALWAYS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
ALWAYS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "NEVER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "NEVER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
NEVER,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "PUSH_OTHER_TEAMS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "PUSH_OTHER_TEAMS")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
PUSH_OTHER_TEAMS,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "PUSH_OWN_TEAM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "PUSH_OWN_TEAM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
PUSH_OWN_TEAM;
|
||||
|
||||
@Override
|
||||
@@ -217,4 +208,9 @@ public class WrappedPlayerTeam extends Wrapper
|
||||
return cast(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_2, to = Versions.V26_3), path = "net.minecraft.world.scores.TeamColor")
|
||||
public static class TeamColor
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import de.eisi05.npc.api.utils.exceptions.VersionNotFound;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.enums.Pose;
|
||||
import de.eisi05.npc.api.wrapper.packets.BundlePacket;
|
||||
import de.eisi05.npc.api.wrapper.packets.ChatPacket;
|
||||
import de.eisi05.npc.api.wrapper.packets.PacketWrapper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -21,8 +20,8 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.server.level.ServerPlayer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.server.level.EntityPlayer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.server.level.ServerPlayer")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.server.level.EntityPlayer")
|
||||
public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
{
|
||||
private static final Map<UUID, WrappedServerPlayer> map = new HashMap<>();
|
||||
@@ -54,9 +53,7 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
|
||||
WrappedServerPlayer wrappedServerPlayer = switch(Versions.getVersion())
|
||||
{
|
||||
case V1_17, V1_18, V1_18_2, V1_19_3, V1_19_4, V1_20 -> createWrappedInstance(WrappedServerPlayer.class, mcServer, serverLevel, gameProfile);
|
||||
case V1_19, V1_19_1 -> createWrappedInstance(WrappedServerPlayer.class, mcServer, serverLevel, gameProfile, null);
|
||||
case V1_20_2, V1_20_4, V1_20_6, V1_21, V1_21_2, V1_21_4, V1_21_5, V1_21_6, V1_21_9, V1_21_11, V26_1 ->
|
||||
case V1_20_6, V1_21, V1_21_2, V1_21_4, V1_21_5, V1_21_6, V1_21_9, V1_21_11, V26_1, V26_2, V26_3 ->
|
||||
{
|
||||
WrappedServerPlayer serverPlayer = createWrappedInstance(WrappedServerPlayer.class, mcServer, serverLevel, gameProfile,
|
||||
WrappedClientInformation.createDefault().getHandle());
|
||||
@@ -67,23 +64,15 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
|
||||
yield serverPlayer;
|
||||
}
|
||||
case NONE -> throw new VersionNotFound();
|
||||
default -> throw new VersionNotFound();
|
||||
};
|
||||
|
||||
if(nameDisplay != null)
|
||||
wrappedServerPlayer.setNameTag(nameDisplay);
|
||||
else if(Versions.isCurrentVersionSmallerThan(Versions.V1_19_4))
|
||||
{
|
||||
WrappedArmorStand armorStand = WrappedArmorStand.create(location.getWorld());
|
||||
armorStand.moveTo(location.clone().add(0, 0.2, 0));
|
||||
wrappedServerPlayer.setNameTag(armorStand);
|
||||
}
|
||||
else
|
||||
{
|
||||
WrappedTextDisplay textDisplay = WrappedTextDisplay.create(location.getWorld());
|
||||
textDisplay.moveTo(location.clone().add(0, 0.2, 0));
|
||||
wrappedServerPlayer.setNameTag(textDisplay);
|
||||
}
|
||||
|
||||
WrappedTextDisplay textDisplay = WrappedTextDisplay.create(location.getWorld());
|
||||
textDisplay.moveTo(location.clone().add(0, 0.2, 0));
|
||||
wrappedServerPlayer.setNameTag(textDisplay);
|
||||
|
||||
wrappedServerPlayer.moveTo(location);
|
||||
wrappedServerPlayer.setListName(name);
|
||||
@@ -97,7 +86,7 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
return create(location, uuid, new GameProfile(uuid, "NPC" + uuid.toString().substring(0, 13)), name, nameDisplay);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getAttribute")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getAttribute")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "f")
|
||||
@@ -106,7 +95,7 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
return new WrappedAttributeInstance(invokeWrappedMethod(attribute));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getGameProfile")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "getGameProfile")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "gI")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "gz")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "gr")
|
||||
@@ -114,132 +103,86 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "gh")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21), path = "fX")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "gb")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "fR")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "fQ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "fM")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_4), path = "fI")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "fD")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_1), path = "fy")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19), path = "fz")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_18_2), path = "fq")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_18), path = "fp")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getProfile")
|
||||
public @NotNull GameProfile getGameProfile()
|
||||
{
|
||||
return invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20, to = Versions.V1_21), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_4), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "c")
|
||||
public @NotNull WrappedConnection playerConnection()
|
||||
{
|
||||
return new WrappedConnection(getWrappedFieldValue());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "connection")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_6, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20, to = Versions.V1_21), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_4), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "c")
|
||||
public void setConnection(@NotNull WrappedConnection newConnection)
|
||||
{
|
||||
setWrappedFieldValue(newConnection.getHandle());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "listName")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "listName")
|
||||
public void setListName(@Nullable WrappedComponent component)
|
||||
{
|
||||
setWrappedFieldValue(component.getHandle());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "listName")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "listName")
|
||||
public void setListName(@Nullable String name)
|
||||
{
|
||||
setListName(WrappedComponent.create(name));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V26_1), path = "listOrder")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "listOrder")
|
||||
public void setListOrder(int order)
|
||||
{
|
||||
setWrappedFieldValue(order);
|
||||
}
|
||||
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_4), path = "e")
|
||||
public void setLatency(int i)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_20_2))
|
||||
setWrappedFieldValue(i);
|
||||
else
|
||||
setConnection(new WrappedConnection(WrappedMinecraftServer.INSTANCE, playerConnection().networkManager(), this,
|
||||
new WrappedConnection.CommonListenerCookie(getGameProfile(), i, true)));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "setPose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "setPose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "setPose")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
public void setPose(@NotNull Pose pose)
|
||||
{
|
||||
invokeWrappedMethod(pose);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "createCommandSourceStack")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "createCommandSourceStack")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "C")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "A")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_5), path = "z")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "A")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "dg")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "dd")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "dc")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "da")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_4), path = "cZ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "cY")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_1), path = "cT")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19), path = "cU")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_18_2), path = "cQ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getCommandListener")
|
||||
public WrappedCommandSourceStack createCommandStack()
|
||||
{
|
||||
return new WrappedCommandSourceStack(invokeWrappedMethod());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "stopUsingItem")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "stopUsingItem")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "gf")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_9), path = "fU")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "fM")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_5), path = "fF")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21), path = "fx")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "fB")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_4), path = "ft")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "fs")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20), path = "fo")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_4), path = "fk")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "ff")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_1), path = "eZ")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19), path = "fa")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_18_2), path = "eS")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_18), path = "eR")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "clearActiveItem")
|
||||
public void stopUsingItem()
|
||||
{
|
||||
invokeWrappedMethod();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "sendSystemMessage")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "sendSystemMessage")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void sendRawMessage(@NotNull WrappedComponent component)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_19))
|
||||
sendPacket(new ChatPacket(component, ChatPacket.ChatType.CHAT));
|
||||
else
|
||||
invokeWrappedMethod(component);
|
||||
invokeWrappedMethod(component);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "remove")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "removePlayerImmediately")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public void remove()
|
||||
{
|
||||
map.remove(getUUID());
|
||||
@@ -248,18 +191,14 @@ public class WrappedServerPlayer extends WrappedEntity<Player>
|
||||
Reflections.invokeMethod(nmsLevel, getPath(), getHandle(), RemovalReason.DISCARDED.getHandle());
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "getAdvancements-stopListening")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_2, to = Versions.V26_3), path = "getAdvancements-clearTriggers")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V26_1), path = "getAdvancements-stopListening")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "U-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_6), path = "S-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_5), path = "R-a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_4), path = "S-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21), path = "R-a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_4, to = Versions.V1_20_6), path = "Q-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_2), path = "O-a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V1_20), path = "M-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_19_3), path = "N-a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_18, to = Versions.V1_19_1), path = "M-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_17), path = "getAdvancementData-a")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_20_6), path = "Q-a")
|
||||
public void stopAdvancementListening()
|
||||
{
|
||||
String path = getPath();
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package de.eisi05.npc.api.wrapper.objects;
|
||||
|
||||
import de.eisi05.npc.api.objects.NameDisplayOptions;
|
||||
import de.eisi05.npc.api.utils.Reflections;
|
||||
import de.eisi05.npc.api.utils.Var;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.TextDisplay;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V26_1), path = "net.minecraft.world.entity.Display$TextDisplay")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.world.entity.Display$TextDisplay")
|
||||
public class WrappedTextDisplay extends WrappedEntity.WrappedNameTag<Entity>
|
||||
{
|
||||
private static final boolean flag = Versions.isCurrentVersionSmallerThan(Versions.V1_20_2);
|
||||
private final Map<WrappedEntityData.EntityDataAccessor<?>, Object> dataMap = new LinkedHashMap<>();
|
||||
|
||||
protected WrappedTextDisplay(Object handle)
|
||||
@@ -47,7 +49,7 @@ public class WrappedTextDisplay extends WrappedEntity.WrappedNameTag<Entity>
|
||||
*/
|
||||
public WrappedTextDisplay translation(float x, float y, float z)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(flag ? 10 : 11), new Vector3f(x, y, z));
|
||||
return set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(11), new Vector3f(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,169 +64,158 @@ public class WrappedTextDisplay extends WrappedEntity.WrappedNameTag<Entity>
|
||||
*/
|
||||
public WrappedTextDisplay scale(float x, float y, float z)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(flag ? 11 : 12), new Vector3f(x, y, z));
|
||||
return set(WrappedEntityData.EntityDataSerializers.VECTOR3.create(12), new Vector3f(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the billboard alignment constraints.
|
||||
* Default: CENTER
|
||||
* Sets the billboard alignment constraints. Default: CENTER
|
||||
*
|
||||
* @param constraints BillboardConstraints enum.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay billboardConstraints(BillboardConstraints constraints)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(flag ? 14 : 15), (byte) constraints.ordinal());
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(15), (byte) constraints.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets brightness override.
|
||||
* Default: -1
|
||||
* Sets brightness override. Default: -1
|
||||
*
|
||||
* @param brightness Brightness value.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay brightnessOverride(int brightness)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(flag ? 15 : 16), brightness);
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(16), brightness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the viewing range of the nametag.
|
||||
* Default: 1.0
|
||||
* Sets the viewing range of the nametag. Default: 1.0
|
||||
*
|
||||
* @param range View range.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay viewRange(float range)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(flag ? 16 : 17), range);
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(17), range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shadow radius.
|
||||
* Default: 0.0
|
||||
* Sets the shadow radius. Default: 0.0
|
||||
*
|
||||
* @param radius Shadow radius.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay shadowRadius(float radius)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(flag ? 17 : 18), radius);
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(18), radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shadow strength.
|
||||
* Default: 1.0
|
||||
* Sets the shadow strength. Default: 1.0
|
||||
*
|
||||
* @param strength Shadow strength.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay shadowStrength(float strength)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(flag ? 18 : 19), strength);
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(19), strength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width of the nametag.
|
||||
* Default: 0.0
|
||||
*
|
||||
* @param width Width value.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay width(float width)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(flag ? 19 : 20), width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the height of the nametag.
|
||||
* Default: 1.0
|
||||
*
|
||||
* @param height Height value.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay height(float height)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.FLOAT.create(flag ? 20 : 21), height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a glow color override.
|
||||
* Default: -1
|
||||
*
|
||||
* @param color Glow color as integer.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay glowColorOverride(int color)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(flag ? 21 : 22), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line width of the text.
|
||||
* Default: 200
|
||||
* Sets the line width of the text. Default: 200
|
||||
*
|
||||
* @param width Line width.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay lineWidth(int width)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(flag ? 23 : 24), width);
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(24), width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background color.
|
||||
* Default: 1073741824 (0x40000000)
|
||||
* Sets the background color. Default: 1073741824 (0x40000000)
|
||||
*
|
||||
* @param color Background color as integer.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay backgroundColor(int color)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(flag ? 24 : 25), color);
|
||||
return set(WrappedEntityData.EntityDataSerializers.INT.create(25), color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text opacity.
|
||||
* Default: -1 (fully opaque)
|
||||
* Sets the text opacity. Default: -1 (fully opaque)
|
||||
*
|
||||
* @param opacity Text opacity.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay textOpacity(byte opacity)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(flag ? 25 : 26), opacity);
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(26), opacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags including shadow, see-through, background color, and alignment.
|
||||
* Default: NONE
|
||||
* Sets flags including shadow, see-through, background color, and alignment. Default: NONE
|
||||
*
|
||||
* @param flags Varargs of TextDisplayFlags.
|
||||
* @return This instance for chaining.
|
||||
*/
|
||||
public WrappedTextDisplay flags(TextDisplayFlags... flags)
|
||||
{
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(flag ? 26 : 27), TextDisplayFlags.combineFlags(flags));
|
||||
return set(WrappedEntityData.EntityDataSerializers.BYTE.create(27), TextDisplayFlags.combineFlags(flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all configured data to the given TextDisplay and component.
|
||||
*
|
||||
* @param component The text component to display.
|
||||
* @param displayOptions The display options to apply.
|
||||
* @return The WrappedEntityData after applying values.
|
||||
*/
|
||||
public WrappedEntityData applyData(WrappedComponent component)
|
||||
public WrappedEntityData applyData(WrappedComponent component, @NotNull NameDisplayOptions displayOptions)
|
||||
{
|
||||
WrappedEntityData data = getEntityData();
|
||||
data.set(WrappedEntityData.EntityDataSerializers.OPTIONAL_CHAT_COMPONENT.create(2), Optional.of(component.getHandle()));
|
||||
data.set(WrappedEntityData.EntityDataSerializers.BOOLEAN.create(4), true);
|
||||
|
||||
translation(0, 0.25f, 0);
|
||||
translation(0, 0.25f + displayOptions.getHeight(), 0);
|
||||
billboardConstraints(BillboardConstraints.CENTER);
|
||||
|
||||
data.set(WrappedEntityData.EntityDataSerializers.CHAT_COMPONENT.create(flag ? 22 : 23), Var.unsafeCast(component.getHandle()));
|
||||
float[] scale = displayOptions.getScale();
|
||||
scale(scale[0], scale[1], scale[2]);
|
||||
|
||||
int brightness = displayOptions.getBrightness();
|
||||
brightnessOverride(brightness);
|
||||
|
||||
float viewRange = displayOptions.getViewRange();
|
||||
viewRange(viewRange);
|
||||
|
||||
int lineWidth = displayOptions.getLineWidth();
|
||||
lineWidth(lineWidth);
|
||||
|
||||
int backgroundColor = displayOptions.getBackgroundColor();
|
||||
backgroundColor(backgroundColor);
|
||||
|
||||
byte textOpacity = displayOptions.getTextOpacity();
|
||||
textOpacity(textOpacity);
|
||||
|
||||
|
||||
boolean isSeeThrough = displayOptions.isSeeThrough();
|
||||
TextDisplay.TextAlignment alignment = displayOptions.getAlignment();
|
||||
TextDisplayFlags alignmentFlag = switch(alignment)
|
||||
{
|
||||
case LEFT -> TextDisplayFlags.LEFT_ALIGNMENT;
|
||||
case CENTER -> TextDisplayFlags.CENTER_ALIGNMENT;
|
||||
case RIGHT -> TextDisplayFlags.RIGHT_ALIGNMENT;
|
||||
};
|
||||
|
||||
if(isSeeThrough)
|
||||
flags(alignmentFlag, TextDisplayFlags.IS_SEE_THROUGH);
|
||||
else
|
||||
flags(alignmentFlag);
|
||||
|
||||
data.set(WrappedEntityData.EntityDataSerializers.CHAT_COMPONENT.create(23), Var.unsafeCast(component.getHandle()));
|
||||
|
||||
dataMap.forEach((accessor, value) -> data.set(accessor, Var.unsafeCast(value)));
|
||||
|
||||
@@ -252,9 +243,10 @@ public class WrappedTextDisplay extends WrappedEntity.WrappedNameTag<Entity>
|
||||
IS_SEE_THROUGH((byte) 0x02),
|
||||
USE_DEFAULT_BACKGROUND_COLOR((byte) 0x04),
|
||||
CENTER_ALIGNMENT((byte) 0x00),
|
||||
LEFT_ALIGNMENT((byte) 0x01),
|
||||
RIGHT_ALIGNMENT((byte) 0x02);
|
||||
LEFT_ALIGNMENT((byte) 0x08),
|
||||
RIGHT_ALIGNMENT((byte) 0x10);
|
||||
|
||||
private static final byte ALIGNMENT_MASK = (byte) 0x18;
|
||||
private final byte flag;
|
||||
|
||||
TextDisplayFlags(byte flag) {this.flag = flag;}
|
||||
@@ -262,8 +254,22 @@ public class WrappedTextDisplay extends WrappedEntity.WrappedNameTag<Entity>
|
||||
public static byte combineFlags(TextDisplayFlags... flags)
|
||||
{
|
||||
byte result = 0;
|
||||
boolean alignmentSet = false;
|
||||
|
||||
for(TextDisplayFlags flag : flags)
|
||||
result |= flag.flag;
|
||||
{
|
||||
boolean isAlignment = (flag == CENTER_ALIGNMENT || flag == LEFT_ALIGNMENT || flag == RIGHT_ALIGNMENT);
|
||||
|
||||
if(isAlignment)
|
||||
{
|
||||
if(alignmentSet)
|
||||
throw new IllegalArgumentException("You cannot clear or set multiple alignments at once!");
|
||||
result = (byte) ((result & ~ALIGNMENT_MASK) | flag.flag);
|
||||
alignmentSet = true;
|
||||
}
|
||||
else
|
||||
result |= flag.flag;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,27 +4,32 @@ import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.phys.Vec2")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.phys.Vec2F")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.phys.Vec2")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.phys.Vec2F")
|
||||
|
||||
public class WrappedVec2F extends Wrapper
|
||||
{
|
||||
public WrappedVec2F(float yaw, float pitch)
|
||||
{
|
||||
super(createInstance(WrappedVec2F.class, yaw, pitch));
|
||||
}
|
||||
|
||||
public WrappedVec2F(Object handle)
|
||||
{
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "x")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "x")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_9), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "i")
|
||||
public float getPitch()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "y")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "y")
|
||||
@Mapping(fixed = @Mapping.Fixed(Versions.V1_21_11), path = "k")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_9), path = "j")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_9), path = "j")
|
||||
public float getYaw()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
|
||||
@@ -6,8 +6,8 @@ import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.world.phys.Vec3")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.world.phys.Vec3D")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.world.phys.Vec3")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.world.phys.Vec3D")
|
||||
public class WrappedVec3D extends Wrapper
|
||||
{
|
||||
private WrappedVec3D(Object handle)
|
||||
@@ -30,28 +30,28 @@ public class WrappedVec3D extends Wrapper
|
||||
return new WrappedVec3D(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "x")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "x")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "g")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_6), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "c")
|
||||
public double getX()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "y")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "y")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "h")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_6), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "d")
|
||||
public double getY()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "z")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "z")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_9, to = Versions.V1_21_11), path = "i")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_6), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21), path = "e")
|
||||
public double getZ()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
|
||||
@@ -2,16 +2,18 @@ package de.eisi05.npc.api.wrapper.packets;
|
||||
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.Wrapper;
|
||||
import de.eisi05.npc.api.wrapper.enums.InteractionHand;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedServerPlayer;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundAnimatePacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutAnimation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundAnimatePacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutAnimation")
|
||||
public class AnimatePacket extends PacketWrapper
|
||||
{
|
||||
protected AnimatePacket(@NotNull WrappedEntity<?> entity, int animationId)
|
||||
@@ -21,8 +23,33 @@ public class AnimatePacket extends PacketWrapper
|
||||
|
||||
public static @Nullable PacketWrapper create(@NotNull WrappedEntity<?> entity, @NotNull Animation animation)
|
||||
{
|
||||
if(animation != Animation.HURT || Versions.isCurrentVersionSmallerThan(Versions.V1_19_4))
|
||||
if(animation != Animation.HURT)
|
||||
{
|
||||
if(!Versions.isCurrentVersionSmallerThan(Versions.V26_3))
|
||||
{
|
||||
return switch(animation)
|
||||
{
|
||||
case WAKE_UP -> new AnimatePacket(entity, 0);
|
||||
case CRITICAL_HIT -> new AnimatePacket(entity, 1);
|
||||
case MAGIC_CRITICAL_HIT -> new AnimatePacket(entity, 2);
|
||||
case SWING_MAIN_HAND -> new SwingAnimationPacket(entity.getId(), InteractionHand.MAIN_HAND,
|
||||
new SwingAnimationPacket.SwingAnimation(SwingAnimationPacket.SwingAnimationType.WHACK,
|
||||
SwingAnimationPacket.SwingAnimation.DEFAULT_DURATION_WHACK));
|
||||
case SWING_OFF_HAND -> new SwingAnimationPacket(entity.getId(), InteractionHand.OFF_HAND,
|
||||
new SwingAnimationPacket.SwingAnimation(SwingAnimationPacket.SwingAnimationType.WHACK,
|
||||
SwingAnimationPacket.SwingAnimation.DEFAULT_DURATION_WHACK));
|
||||
case STAB_MAIN_HAND -> new SwingAnimationPacket(entity.getId(), InteractionHand.MAIN_HAND,
|
||||
new SwingAnimationPacket.SwingAnimation(SwingAnimationPacket.SwingAnimationType.STAB,
|
||||
SwingAnimationPacket.SwingAnimation.DEFAULT_DURATION_STAB));
|
||||
case STAB_OFF_HAND -> new SwingAnimationPacket(entity.getId(), InteractionHand.OFF_HAND,
|
||||
new SwingAnimationPacket.SwingAnimation(SwingAnimationPacket.SwingAnimationType.STAB,
|
||||
SwingAnimationPacket.SwingAnimation.DEFAULT_DURATION_STAB));
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
return new AnimatePacket(entity, animation.ordinal());
|
||||
}
|
||||
|
||||
if(!(entity.getBukkitPlayer() instanceof LivingEntity))
|
||||
return null;
|
||||
@@ -37,10 +64,25 @@ public class AnimatePacket extends PacketWrapper
|
||||
WAKE_UP,
|
||||
SWING_OFF_HAND,
|
||||
CRITICAL_HIT,
|
||||
MAGIC_CRITICAL_HIT
|
||||
MAGIC_CRITICAL_HIT,
|
||||
/**
|
||||
* @since 26.3
|
||||
*/
|
||||
STAB_MAIN_HAND,
|
||||
/**
|
||||
* @since 26.3
|
||||
*/
|
||||
STAB_OFF_HAND;
|
||||
|
||||
public static Animation[] getCompatibleAnimations()
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V26_3))
|
||||
return Arrays.stream(values()).filter(animation -> animation != STAB_MAIN_HAND && animation != STAB_OFF_HAND).toArray(Animation[]::new);
|
||||
return values();
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_4, to = Versions.V26_1),
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3),
|
||||
path = "net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket")
|
||||
public static class HurtAnimationPacket extends PacketWrapper
|
||||
{
|
||||
@@ -49,4 +91,44 @@ public class AnimatePacket extends PacketWrapper
|
||||
super(HurtAnimationPacket.class, entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundSwingAnimationPacket")
|
||||
public static class SwingAnimationPacket extends PacketWrapper
|
||||
{
|
||||
protected SwingAnimationPacket(int entityId, InteractionHand hand, SwingAnimation animation)
|
||||
{
|
||||
super(createInstance(SwingAnimationPacket.class, entityId, hand.getHandle(), animation.getHandle()));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "net.minecraft.world.item.component.SwingAnimation")
|
||||
public static class SwingAnimation extends Wrapper
|
||||
{
|
||||
private static final int DEFAULT_DURATION_WHACK = 6;
|
||||
private static final int DEFAULT_DURATION_STAB = 12;
|
||||
|
||||
public SwingAnimation(SwingAnimationType type, int duration)
|
||||
{
|
||||
super(createInstance(SwingAnimation.class, type.getHandle(), duration));
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "net.minecraft.world.item.SwingAnimationType")
|
||||
public enum SwingAnimationType implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "NONE")
|
||||
NONE,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "WHACK")
|
||||
WHACK,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_3, to = Versions.V26_3), path = "STAB")
|
||||
STAB;
|
||||
|
||||
@Override
|
||||
public @NotNull Object getHandle()
|
||||
{
|
||||
return cast(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package de.eisi05.npc.api.wrapper.packets;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ServerboundAttackPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ServerboundAttackPacket")
|
||||
public class AttackPacket extends PacketWrapper.PacketHolder
|
||||
{
|
||||
protected AttackPacket(Object handle)
|
||||
@@ -11,7 +11,7 @@ public class AttackPacket extends PacketWrapper.PacketHolder
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "entityId")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "entityId")
|
||||
public int getId()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package de.eisi05.npc.api.wrapper.packets;
|
||||
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "net.minecraft.network.protocol.game.PacketPlayOutChat")
|
||||
public class ChatPacket extends PacketWrapper
|
||||
{
|
||||
private static final UUID NULL_UUID = new UUID(0, 0);
|
||||
|
||||
public ChatPacket(@NotNull WrappedComponent component, @NotNull ChatType type)
|
||||
{
|
||||
super(ChatPacket.class, component, type, NULL_UUID);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "net.minecraft.network.chat.ChatMessageType")
|
||||
public enum ChatType implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "a")
|
||||
CHAT,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "b")
|
||||
SYSTEM,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_18_2), path = "c")
|
||||
GAME_INFO;
|
||||
|
||||
@Override
|
||||
public @NotNull Object getHandle()
|
||||
{
|
||||
return cast(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ package de.eisi05.npc.api.wrapper.packets;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntity")
|
||||
public class MoveEntityPacket
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
".PacketPlayOutEntity$PacketPlayOutEntityLook")
|
||||
public static class Rot extends PacketWrapper
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public abstract class PacketWrapper extends Wrapper
|
||||
super(createInstance(clazz, args));
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V26_1), path = "net.minecraft.network.protocol.Packet")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.network.protocol.Packet")
|
||||
public static abstract class PacketHolder extends PacketWrapper
|
||||
{
|
||||
public PacketHolder(Object handle)
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V26_1), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.network.protocol.game" +
|
||||
".ClientboundPlayerInfoRemovePacket")
|
||||
public class PlayerInfoRemovePacket extends PacketWrapper
|
||||
{
|
||||
|
||||
@@ -5,9 +5,8 @@ import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedServerPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V26_1), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "net.minecraft.network.protocol.game" +
|
||||
".ClientboundPlayerInfoUpdatePacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo")
|
||||
public class PlayerInfoUpdatePacket extends PacketWrapper
|
||||
{
|
||||
public PlayerInfoUpdatePacket(@NotNull Action action, @NotNull WrappedServerPlayer serverPlayer)
|
||||
@@ -15,50 +14,41 @@ public class PlayerInfoUpdatePacket extends PacketWrapper
|
||||
super(PlayerInfoUpdatePacket.class, action, serverPlayer);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game" +
|
||||
".ClientboundPlayerInfoUpdatePacket$Action")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
".ClientboundPlayerInfoUpdatePacket$a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "net.minecraft.network.protocol.game" +
|
||||
".PacketPlayOutPlayerInfo$EnumPlayerInfoAction")
|
||||
public enum Action implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "ADD_PLAYER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "ADD_PLAYER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
ADD_PLAYER,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "INITIALIZE_CHAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "INITIALIZE_CHAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
INITIALIZE_CHAT,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_GAME_MODE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_GAME_MODE")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
UPDATE_GAME_MODE,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_LISTED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_LISTED")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "d")
|
||||
UPDATE_LISTED,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_LATENCY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_LATENCY")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
UPDATE_LATENCY,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_DISPLAY_NAME")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V1_21_11), path = "f")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "d")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_DISPLAY_NAME")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "f")
|
||||
UPDATE_DISPLAY_NAME,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "REMOVE_PLAYER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "e")
|
||||
REMOVE_PLAYER,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_LIST_ORDER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_LIST_ORDER")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V1_21_11), path = "g")
|
||||
UPDATE_LIST_ORDER,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "UPDATE_HAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "UPDATE_HAT")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_4, to = Versions.V1_21_11), path = "h")
|
||||
UPDATE_HAT;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package de.eisi05.npc.api.wrapper.packets;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy")
|
||||
public class RemoveEntityPacket extends PacketWrapper
|
||||
{
|
||||
public RemoveEntityPacket(int entityId)
|
||||
|
||||
@@ -5,8 +5,8 @@ import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundRotateHeadPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundRotateHeadPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation")
|
||||
public class RotateHeadPacket extends PacketWrapper
|
||||
{
|
||||
public RotateHeadPacket(@NotNull WrappedEntity entity, byte yHeadRot)
|
||||
|
||||
@@ -8,19 +8,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata")
|
||||
public class SetEntityDataPacket extends PacketWrapper
|
||||
{
|
||||
public List<?> data;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_19_1), path = "")
|
||||
private SetEntityDataPacket(int id, @NotNull WrappedEntityData data, boolean nonDefaults)
|
||||
{
|
||||
super(SetEntityDataPacket.class, id, data, nonDefaults);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_19_3, to = Versions.V26_1), path = "")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V26_3), path = "")
|
||||
private SetEntityDataPacket(int id, @NotNull List<?> data)
|
||||
{
|
||||
super(SetEntityDataPacket.class, id, data);
|
||||
@@ -29,12 +23,13 @@ public class SetEntityDataPacket extends PacketWrapper
|
||||
|
||||
public static SetEntityDataPacket create(int id, @NotNull WrappedEntityData data)
|
||||
{
|
||||
if(Versions.isCurrentVersionSmallerThan(Versions.V1_19_3))
|
||||
return new SetEntityDataPacket(id, data, true);
|
||||
else
|
||||
{
|
||||
List<?> dataList = data.packDirty();
|
||||
return new SetEntityDataPacket(id, dataList == null ? new ArrayList<>() : dataList);
|
||||
}
|
||||
List<?> dataList = data.packDirty();
|
||||
return new SetEntityDataPacket(id, dataList == null ? new ArrayList<>() : dataList);
|
||||
}
|
||||
|
||||
public static SetEntityDataPacket createNoneDefaults(int id, @NotNull WrappedEntityData data)
|
||||
{
|
||||
List<?> dataList = data.getAll();
|
||||
return new SetEntityDataPacket(id, dataList == null ? new ArrayList<>() : dataList);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment")
|
||||
public class SetEquipmentPacket extends PacketWrapper
|
||||
{
|
||||
public SetEquipmentPacket(int entity, @NotNull List<Pair<?, ?>> list)
|
||||
|
||||
@@ -4,8 +4,8 @@ import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
import de.eisi05.npc.api.wrapper.objects.WrappedEntity;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundSetPassengersPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutMount")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundSetPassengersPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutMount")
|
||||
public class SetPassengerPacket extends PacketWrapper
|
||||
{
|
||||
public SetPassengerPacket(WrappedEntity<?> entity)
|
||||
|
||||
@@ -10,8 +10,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam")
|
||||
public class SetPlayerTeamPacket extends PacketWrapper
|
||||
{
|
||||
private SetPlayerTeamPacket(@NotNull String teamName, int method, @NotNull Optional<Object> parameter, @NotNull Collection<String> players)
|
||||
@@ -41,9 +41,9 @@ public class SetPlayerTeamPacket extends PacketWrapper
|
||||
REMOVE
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game" +
|
||||
".ClientboundSetPlayerTeamPacket$Parameters")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$b")
|
||||
public static class Parameters extends Wrapper
|
||||
{
|
||||
public Parameters(@NotNull WrappedPlayerTeam team)
|
||||
|
||||
@@ -11,8 +11,8 @@ import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport")
|
||||
public class TeleportEntityPacket extends PacketWrapper
|
||||
{
|
||||
public TeleportEntityPacket(WrappedEntity<?> entity, PositionMoveRotation positionMoveRotation, Set<?> relatives, boolean onGround)
|
||||
@@ -35,7 +35,7 @@ public class TeleportEntityPacket extends PacketWrapper
|
||||
return createInstance(TeleportEntityPacket.class, entity.getId(), positionMoveRotation.getHandle(), relatives, onGround);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V26_1), path = "net.minecraft.world.entity.PositionMoveRotation")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21_2, to = Versions.V26_3), path = "net.minecraft.world.entity.PositionMoveRotation")
|
||||
public static class PositionMoveRotation extends Wrapper
|
||||
{
|
||||
private final Vector position;
|
||||
|
||||
@@ -6,7 +6,7 @@ import de.eisi05.npc.api.wrapper.objects.WrappedAttributeInstance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes")
|
||||
public class UpdateAttributesPacket extends PacketWrapper
|
||||
{
|
||||
|
||||
@@ -7,12 +7,11 @@ import de.eisi05.npc.api.wrapper.enums.InteractionHand;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ServerboundInteractPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayInUseEntity")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ServerboundInteractPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayInUseEntity")
|
||||
public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "e")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_20_4), path = "d")
|
||||
public final static Action ATTACK_ACTION = new Action(getStaticWrappedFieldValue("ATTACK_ACTION").orElse(null));
|
||||
|
||||
protected UseEntityPacketWrapper(Object handle)
|
||||
@@ -20,15 +19,14 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "entityId")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "entityId")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_20_4), path = "a")
|
||||
public int getId()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "hand")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "hand")
|
||||
public @Nullable InteractionHand getHand()
|
||||
{
|
||||
Object enumHandle = getWrappedFieldValue();
|
||||
@@ -40,14 +38,13 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
return null;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "usingSecondaryAction")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "usingSecondaryAction")
|
||||
public boolean usingSecondaryAction()
|
||||
{
|
||||
return getWrappedFieldValue();
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_21, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_20_6), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
public @NotNull Action getAction()
|
||||
{
|
||||
Object value = getWrappedFieldValue();
|
||||
@@ -58,17 +55,17 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
return new Action(value);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
".PacketPlayInUseEntity$b")
|
||||
public enum ActionType implements EnumWrapper
|
||||
{
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
INTERACT,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "b")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "b")
|
||||
ATTACK,
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "c")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "c")
|
||||
INTERACT_AT;
|
||||
|
||||
@Override
|
||||
@@ -78,7 +75,7 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
}
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game" +
|
||||
".PacketPlayInUseEntity$EnumEntityUseAction")
|
||||
public static class Action extends Wrapper
|
||||
{
|
||||
@@ -87,7 +84,7 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
super(handle);
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @Nullable ActionType getActionType()
|
||||
{
|
||||
Object enumHandle = invokeWrappedMethod();
|
||||
@@ -99,7 +96,7 @@ public class UseEntityPacketWrapper extends PacketWrapper.PacketHolder
|
||||
return null;
|
||||
}
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "a")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "a")
|
||||
public @Nullable InteractionHand getHand()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -3,8 +3,8 @@ package de.eisi05.npc.api.wrapper.packets;
|
||||
import de.eisi05.npc.api.utils.Versions;
|
||||
import de.eisi05.npc.api.wrapper.Mapping;
|
||||
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_1), path = "net.minecraft.network.protocol.game.ServerboundUseItemPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_17, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayInUseItem")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V26_1, to = Versions.V26_3), path = "net.minecraft.network.protocol.game.ServerboundUseItemPacket")
|
||||
@Mapping(range = @Mapping.Range(from = Versions.V1_20_6, to = Versions.V1_21_11), path = "net.minecraft.network.protocol.game.PacketPlayInUseItem")
|
||||
public class UseItemPacketWrapper extends PacketWrapper.PacketHolder
|
||||
{
|
||||
protected UseItemPacketWrapper(Object handle)
|
||||
|
||||
Reference in new issue
Block a user