Merge pull request #166 from D3v1s0m/feat/health_attribute

Health properties and version command
This commit is contained in:
Pyr 2024-12-28 09:33:40 +00:00 committed by GitHub
commit f1cedb3836
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 222 additions and 0 deletions

View file

@ -30,10 +30,28 @@ dependencies {
implementation project(":api")
}
ext {
gitBranch = System.getenv('GIT_BRANCH') ?: ''
gitCommitHash = System.getenv('GIT_COMMIT') ?: ''
buildId = System.getenv('BUILD_ID') ?: ''
}
shadowJar {
archivesBaseName = "ZNPCsPlus"
archiveClassifier.set ""
manifest {
if (gitBranch?.trim()) {
attributes('Git-Branch': gitBranch)
}
if (gitCommitHash?.trim()) {
attributes('Git-Commit': gitCommitHash)
}
if (buildId?.trim()) {
attributes('Build-Id': buildId)
}
}
relocate "org.objectweb.asm", "lol.pyr.znpcsplus.libraries.asm"
relocate "me.lucko.jarrelocator", "lol.pyr.znpcsplus.libraries.jarrelocator"

View file

@ -342,6 +342,7 @@ public class ZNpcsPlus {
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
.addSubcommand("edit", new ActionEditCommand(npcRegistry, actionRegistry))
.addSubcommand("list", new ActionListCommand(npcRegistry)))
.addSubcommand("version", new VersionCommand(this))
);
}

View file

@ -0,0 +1,64 @@
package lol.pyr.znpcsplus.commands;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
public class VersionCommand implements CommandHandler {
private final String pluginVersion;
private final String gitBranch;
private final String gitCommitHash;
private final String buildId;
public VersionCommand(ZNpcsPlus plugin) {
pluginVersion = plugin.getDescription().getVersion();
String gitBranch = "";
String gitCommitHash = "";
String buildId = "";
try {
URL jarUrl = getClass().getProtectionDomain().getCodeSource().getLocation();
JarFile jarFile = new JarFile(jarUrl.toURI().getPath());
Attributes attributes = jarFile.getManifest().getMainAttributes();
gitBranch = attributes.getValue("Git-Branch");
gitCommitHash = attributes.getValue("Git-Commit-Hash");
buildId = attributes.getValue("Build-Id");
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
this.gitBranch = gitBranch;
this.gitCommitHash = gitCommitHash;
this.buildId = buildId;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
StringBuilder versionBuilder = new StringBuilder("This server is running ZNPCsPlus version ").append(pluginVersion);
if (gitBranch != null && !gitBranch.isEmpty()) {
versionBuilder.append("-").append(gitBranch);
}
if (gitCommitHash != null && !gitCommitHash.isEmpty()) {
versionBuilder.append("@").append(gitCommitHash);
}
if (buildId != null && !buildId.isEmpty()) {
versionBuilder.append(" (Build #").append(buildId).append(")");
}
String version = versionBuilder.toString();
context.send(Component.text(version, NamedTextColor.GREEN)
.hoverEvent(Component.text("Click to copy version to clipboard"))
.clickEvent(ClickEvent.copyToClipboard(version)));
}
}

View file

@ -11,6 +11,7 @@ import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
@ -124,6 +125,15 @@ public class PropertySetCommand implements CommandHandler {
value = context.parse(type);
valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString();
}
else if (property instanceof AttributeProperty) {
value = context.parse(type);
if ((double) value < ((AttributeProperty) property).getMinValue() || (double) value > ((AttributeProperty) property).getMaxValue()) {
double sanitizedValue = ((AttributeProperty) property).sanitizeValue((double) value);
context.send(Component.text("WARNING: Value " + value + " is out of range for property " + property.getName() + ", setting to " + sanitizedValue, NamedTextColor.YELLOW));
value = sanitizedValue;
}
valueName = String.valueOf(value);
}
else {
try {
value = context.parse(type);

View file

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.entity;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
@ -15,6 +16,7 @@ import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.properties.*;
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
@ -154,6 +156,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
linkProperties("glow", "fire", "invisible");
register(new BooleanProperty("silent", 4, false, legacyBooleans));
// Attribute Max Health
register(new AttributeProperty(packetFactory, "attribute_max_health", Attributes.MAX_HEALTH));
// Health - LivingEntity
int healthIndex = 6;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) healthIndex = 9;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) healthIndex = 8;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) healthIndex = 7;
register(new HealthProperty(healthIndex));
final int tameableIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tameableIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) tameableIndex = 16;

View file

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class HealthProperty extends EntityPropertyImpl<Float> {
private final int index;
public HealthProperty(int index) {
super("health", 20f, Float.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
float health = entity.getProperty(this);
health = (float) Attributes.MAX_HEALTH.sanitizeValue(health);
properties.put(index, new EntityData(index, EntityDataTypes.FLOAT, health));
}
}

View file

@ -0,0 +1,63 @@
package lol.pyr.znpcsplus.entity.properties.attributes;
import com.github.retrooper.packetevents.protocol.attribute.Attribute;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AttributeProperty extends EntityPropertyImpl<Double> {
private final PacketFactory packetFactory;
private final Attribute attribute;
public AttributeProperty(PacketFactory packetFactory, String name, Attribute attribute) {
super(name, attribute.getDefaultValue(), Double.class);
this.packetFactory = packetFactory;
this.attribute = attribute;
}
public double getMinValue() {
return attribute.getMinValue();
}
public double getMaxValue() {
return attribute.getMaxValue();
}
public double sanitizeValue(double value) {
return attribute.sanitizeValue(value);
}
@Override
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
apply(player, packetEntity, isSpawned, Collections.emptyList());
return Collections.emptyList();
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
}
public void apply(Player player, PacketEntity entity, boolean isSpawned, List<WrapperPlayServerUpdateAttributes.Property> properties) {
Double value = entity.getProperty(this);
if (value == null) {
return;
}
value = attribute.sanitizeValue(value);
if (isSpawned) {
packetFactory.sendAttribute(player, entity, new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
} else {
properties.add(new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
}
}
public Attribute getAttribute() {
return attribute;
}
}

View file

@ -120,6 +120,9 @@ public class NpcTypeImpl implements NpcType {
"player_knockback_horizontal", "player_knockback_cooldown", "player_knockback_sound", "player_knockback_sound_name",
"player_knockback_sound_volume", "player_knockback_sound_pitch");
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) {
addProperties("health", "attribute_max_health");
}
// TODO: make this look nicer after completing the rest of the properties
if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow");
if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) {

View file

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.player.Equipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.util.NamedColor;
@ -25,4 +26,6 @@ public interface PacketFactory {
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
void sendHandSwing(Player player, PacketEntity entity, boolean offHand);
void setPassengers(Player player, int vehicle, int... passengers);
void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties);
void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property);
}

View file

@ -1,6 +1,7 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
@ -27,6 +28,7 @@ public class V1_17PacketFactory extends V1_8PacketFactory {
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
sendAllMetadata(player, entity, properties);
if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
}
}

View file

@ -36,6 +36,7 @@ public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
sendAllMetadata(player, entity, properties);
sendAllAttributes(player, entity, properties);
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
});
}

View file

@ -18,6 +18,7 @@ import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
import lol.pyr.znpcsplus.util.NamedColor;
@ -55,6 +56,7 @@ public class V1_8PacketFactory implements PacketFactory {
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
sendAllMetadata(player, entity, properties);
sendAllAttributes(player, entity, properties);
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
});
}
@ -70,6 +72,7 @@ public class V1_8PacketFactory implements PacketFactory {
new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location),
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
sendAllMetadata(player, entity, properties);
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
}
@ -187,4 +190,19 @@ public class V1_8PacketFactory implements PacketFactory {
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_OFF_HAND :
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_MAIN_ARM));
}
@Override
public void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties) {
List<WrapperPlayServerUpdateAttributes.Property> attributesList = new ArrayList<>();
properties.getAppliedProperties()
.stream()
.filter(property -> property instanceof AttributeProperty)
.forEach(property -> ((AttributeProperty) property).apply(player, entity, false, attributesList));
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), attributesList));
}
@Override
public void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property) {
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), Collections.singletonList(property)));
}
}