Replace entity tracked fields as well as chunk map ones

This commit is contained in:
fullwall 2024-07-12 17:25:26 +08:00
parent ec51df24b5
commit 6d18bc16c7
8 changed files with 100 additions and 57 deletions

View File

@ -912,8 +912,8 @@ public class NPCCommands {
for (String part : parts) { for (String part : parts) {
if (part.contains(":")) { if (part.contains(":")) {
int idx = part.indexOf(':'); int idx = part.indexOf(':');
Template template = templateRegistry.getTemplateByKey( Template template = templateRegistry.getTemplateByKey(new NamespacedKey(part.substring(0, idx),
new NamespacedKey(part.substring(0, idx), part.substring(idx + 1).toLowerCase(Locale.US))); part.substring(idx + 1).toLowerCase(Locale.ROOT)));
if (template == null) if (template == null)
continue; continue;
template.apply(npc); template.apply(npc);
@ -1263,7 +1263,7 @@ public class NPCCommands {
@Command( @Command(
aliases = { "npc" }, aliases = { "npc" },
usage = "hologram add [text] | set [line #] [text] | remove [line #] | bgcolor [line #] (color) | clear | lineheight [height] | viewrange [range] | margintop [line #] [margin] | marginbottom [line #] [margin]", usage = "hologram add [text] | set [line #] [text] | remove [line #] | bgcolor [line #] (red,green,blue(,alpha)) | clear | lineheight [height] | viewrange [range] | margintop [line #] [margin] | marginbottom [line #] [margin]",
desc = "", desc = "",
modifiers = { "hologram" }, modifiers = { "hologram" },
min = 1, min = 1,
@ -3661,7 +3661,7 @@ public class NPCCommands {
trait.setInterested(!trait.isInterested()); trait.setInterested(!trait.isInterested());
} }
if (variant != null) { if (variant != null) {
variant = variant.toUpperCase(Locale.US); variant = variant.toUpperCase(Locale.ROOT);
try { try {
Wolf.Variant.class.getField(variant); Wolf.Variant.class.getField(variant);
} catch (Throwable t) { } catch (Throwable t) {

View File

@ -44,7 +44,7 @@ public class TemplateCommands {
if (templateKey.contains(":")) { if (templateKey.contains(":")) {
int idx = templateKey.indexOf(':'); int idx = templateKey.indexOf(':');
template = registry.getTemplateByKey(new NamespacedKey(templateKey.substring(0, idx), template = registry.getTemplateByKey(new NamespacedKey(templateKey.substring(0, idx),
templateKey.substring(idx + 1).toLowerCase(Locale.US))); templateKey.substring(idx + 1).toLowerCase(Locale.ROOT)));
} else { } else {
Collection<Template> templates = registry.getTemplates(templateKey); Collection<Template> templates = registry.getTemplates(templateKey);
if (templates.isEmpty()) if (templates.isEmpty())
@ -73,9 +73,9 @@ public class TemplateCommands {
@Arg(value = 1, completionsProvider = TemplateCompletions.class) String templateName) @Arg(value = 1, completionsProvider = TemplateCompletions.class) String templateName)
throws CommandException { throws CommandException {
int idx = templateName.indexOf(':'); int idx = templateName.indexOf(':');
NamespacedKey key = idx == -1 ? new NamespacedKey("generated", templateName.toLowerCase(Locale.US)) NamespacedKey key = idx == -1 ? new NamespacedKey("generated", templateName.toLowerCase(Locale.ROOT))
: new NamespacedKey(templateName.substring(0, idx), : new NamespacedKey(templateName.substring(0, idx),
templateName.substring(idx + 1).toLowerCase(Locale.US)); templateName.substring(idx + 1).toLowerCase(Locale.ROOT));
if (registry.getTemplateByKey(key) != null) if (registry.getTemplateByKey(key) != null)
throw new CommandException(Messages.TEMPLATE_CONFLICT); throw new CommandException(Messages.TEMPLATE_CONFLICT);
registry.generateTemplateFromNPC(key, npc); registry.generateTemplateFromNPC(key, npc);

View File

@ -20,7 +20,7 @@ import org.bukkit.scheduler.BukkitRunnable;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables; import com.google.common.collect.ObjectArrays;
import com.google.common.collect.SetMultimap; import com.google.common.collect.SetMultimap;
import net.citizensnpcs.NPCNeedsRespawnEvent; import net.citizensnpcs.NPCNeedsRespawnEvent;
@ -381,7 +381,8 @@ public class CitizensNPC extends AbstractNPC {
return; return;
} }
navigator.onSpawn(); navigator.onSpawn();
for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) {
for (Trait trait : traits.values().toArray(ObjectArrays.newArray(Trait.class, traits.size()))) {
try { try {
trait.onSpawn(); trait.onSpawn();
} catch (Throwable ex) { } catch (Throwable ex) {
@ -389,7 +390,6 @@ public class CitizensNPC extends AbstractNPC {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
// Replace the entity tracker
NMS.replaceTracker(getEntity()); NMS.replaceTracker(getEntity());
EntityType type = getEntity().getType(); EntityType type = getEntity().getType();
if (type.isAlive()) { if (type.isAlive()) {
@ -479,21 +479,21 @@ public class CitizensNPC extends AbstractNPC {
resetCachedCoord(); resetCachedCoord();
return; return;
} }
Location loc = getEntity().getLocation();
if (data().has(NPC.Metadata.ACTIVATION_RANGE)) { if (data().has(NPC.Metadata.ACTIVATION_RANGE)) {
int range = data().get(NPC.Metadata.ACTIVATION_RANGE); int range = data().get(NPC.Metadata.ACTIVATION_RANGE);
if (range == -1 || CitizensAPI.getLocationLookup().getNearbyPlayers(getStoredLocation(), range) if (range == -1 || CitizensAPI.getLocationLookup().getNearbyPlayers(loc, range).iterator().hasNext()) {
.iterator().hasNext()) {
NMS.activate(getEntity()); NMS.activate(getEntity());
} }
} }
boolean shouldSwim = data().get(NPC.Metadata.SWIM, SwimmingExaminer.isWaterMob(getEntity())) boolean shouldSwim = data().get(NPC.Metadata.SWIM, SwimmingExaminer.isWaterMob(getEntity()))
&& MinecraftBlockExaminer.isLiquid(getEntity().getLocation().getBlock().getType()); && MinecraftBlockExaminer.isLiquid(loc.getBlock().getType());
if (navigator.isNavigating()) { if (navigator.isNavigating()) {
if (shouldSwim) { if (shouldSwim) {
getEntity().setVelocity(getEntity().getVelocity().multiply( getEntity().setVelocity(getEntity().getVelocity().multiply(
data().get(NPC.Metadata.WATER_SPEED_MODIFIER, Setting.NPC_WATER_SPEED_MODIFIER.asFloat()))); data().get(NPC.Metadata.WATER_SPEED_MODIFIER, Setting.NPC_WATER_SPEED_MODIFIER.asFloat())));
Location currentDest = navigator.getPathStrategy().getCurrentDestination(); Location currentDest = navigator.getPathStrategy().getCurrentDestination();
if (currentDest == null || currentDest.getY() > getStoredLocation().getY()) { if (currentDest == null || currentDest.getY() > loc.getY()) {
NMS.trySwim(getEntity()); NMS.trySwim(getEntity());
} }
} }
@ -504,23 +504,15 @@ public class CitizensNPC extends AbstractNPC {
} }
} }
if (SUPPORT_GLOWING && data().has(NPC.Metadata.GLOWING)) { if (SUPPORT_GLOWING && data().has(NPC.Metadata.GLOWING)) {
try {
getEntity().setGlowing(data().get(NPC.Metadata.GLOWING, false)); getEntity().setGlowing(data().get(NPC.Metadata.GLOWING, false));
} catch (NoSuchMethodError e) {
SUPPORT_GLOWING = false;
}
} }
if (SUPPORT_SILENT && data().has(NPC.Metadata.SILENT)) { if (SUPPORT_SILENT && data().has(NPC.Metadata.SILENT)) {
try {
getEntity().setSilent(Boolean.parseBoolean(data().get(NPC.Metadata.SILENT).toString())); getEntity().setSilent(Boolean.parseBoolean(data().get(NPC.Metadata.SILENT).toString()));
} catch (NoSuchMethodError e) {
SUPPORT_SILENT = false;
}
} }
boolean isLiving = getEntity() instanceof LivingEntity; boolean isLiving = getEntity() instanceof LivingEntity;
if (isUpdating(NPCUpdate.PACKET)) { if (isUpdating(NPCUpdate.PACKET)) {
if (data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean())) { if (data().get(NPC.Metadata.KEEP_CHUNK_LOADED, Setting.KEEP_CHUNKS_LOADED.asBoolean())) {
ChunkCoord currentCoord = new ChunkCoord(getStoredLocation()); ChunkCoord currentCoord = new ChunkCoord(loc);
if (!currentCoord.equals(cachedCoord)) { if (!currentCoord.equals(cachedCoord)) {
resetCachedCoord(); resetCachedCoord();
currentCoord.setForceLoaded(true); currentCoord.setForceLoaded(true);
@ -538,11 +530,7 @@ public class CitizensNPC extends AbstractNPC {
if (isLiving) { if (isLiving) {
NMS.setKnockbackResistance((LivingEntity) getEntity(), isProtected() ? 1D : 0D); NMS.setKnockbackResistance((LivingEntity) getEntity(), isProtected() ? 1D : 0D);
if (SUPPORT_PICKUP_ITEMS) { if (SUPPORT_PICKUP_ITEMS) {
try {
((LivingEntity) getEntity()).setCanPickupItems(data().get(NPC.Metadata.PICKUP_ITEMS, false)); ((LivingEntity) getEntity()).setCanPickupItems(data().get(NPC.Metadata.PICKUP_ITEMS, false));
} catch (Throwable t) {
SUPPORT_PICKUP_ITEMS = false;
}
} }
if (getEntity() instanceof Player) { if (getEntity() instanceof Player) {
updateUsingItemState((Player) getEntity()); updateUsingItemState((Player) getEntity());
@ -622,9 +610,31 @@ public class CitizensNPC extends AbstractNPC {
} }
private static SetMultimap<ChunkCoord, NPC> CHUNK_LOADERS = HashMultimap.create(); private static SetMultimap<ChunkCoord, NPC> CHUNK_LOADERS = HashMultimap.create();
private static boolean SUPPORT_GLOWING = true; private static boolean SUPPORT_GLOWING = false;
private static boolean SUPPORT_NODAMAGE_TICKS = true; private static boolean SUPPORT_NODAMAGE_TICKS = false;
private static boolean SUPPORT_PICKUP_ITEMS = true; private static boolean SUPPORT_PICKUP_ITEMS = false;
private static boolean SUPPORT_SILENT = true; private static boolean SUPPORT_SILENT = false;
private static boolean SUPPORT_USE_ITEM = true; private static boolean SUPPORT_USE_ITEM = true;
static {
try {
Entity.class.getMethod("setNoDamageTicks", int.class);
SUPPORT_NODAMAGE_TICKS = true;
} catch (NoSuchMethodException | SecurityException e) {
}
try {
Entity.class.getMethod("setGlowing", boolean.class);
SUPPORT_GLOWING = true;
} catch (NoSuchMethodException | SecurityException e) {
}
try {
Entity.class.getMethod("setSilent", boolean.class);
SUPPORT_SILENT = true;
} catch (NoSuchMethodException | SecurityException e) {
}
try {
LivingEntity.class.getMethod("setCanPickupItems", boolean.class);
SUPPORT_PICKUP_ITEMS = true;
} catch (NoSuchMethodException | SecurityException e) {
}
}
} }

View File

@ -177,7 +177,7 @@ public class CitizensTraitFactory implements TraitFactory {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends Trait> T getTrait(String name) { public <T extends Trait> T getTrait(String name) {
TraitInfo info = registered.get(name.toLowerCase(Locale.US)); TraitInfo info = registered.get(name.toLowerCase(Locale.ROOT));
if (info == null) if (info == null)
return null; return null;
return (T) create(info); return (T) create(info);
@ -185,7 +185,7 @@ public class CitizensTraitFactory implements TraitFactory {
@Override @Override
public Class<? extends Trait> getTraitClass(String name) { public Class<? extends Trait> getTraitClass(String name) {
TraitInfo info = registered.get(name.toLowerCase(Locale.US)); TraitInfo info = registered.get(name.toLowerCase(Locale.ROOT));
return info == null ? null : info.getTraitClass(); return info == null ? null : info.getTraitClass();
} }

View File

@ -1,7 +1,5 @@
package net.citizensnpcs.trait; package net.citizensnpcs.trait;
import java.lang.invoke.MethodHandle;
import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.ChestedHorse;
import org.bukkit.entity.Horse; import org.bukkit.entity.Horse;
import org.bukkit.entity.Horse.Color; import org.bukkit.entity.Horse.Color;
@ -111,22 +109,19 @@ public class HorseModifiers extends Trait {
horse.getInventory().setArmor(armor); horse.getInventory().setArmor(armor);
horse.getInventory().setSaddle(saddle); horse.getInventory().setSaddle(saddle);
} }
if (CARRYING_CHEST_METHOD == null) if (SUPPORTS_CARRYING_CHEST && npc.getEntity() instanceof ChestedHorse) {
return; ((ChestedHorse) npc.getEntity()).setCarryingChest(carryingChest);
if (npc.getEntity() instanceof ChestedHorse) {
try {
CARRYING_CHEST_METHOD.invoke(npc.getEntity(), carryingChest);
} catch (Throwable e) {
}
} }
} }
private static MethodHandle CARRYING_CHEST_METHOD; private static boolean SUPPORTS_CARRYING_CHEST;
static { static {
try { try {
CARRYING_CHEST_METHOD = NMS.getMethodHandle(Class.forName("org.bukkit.entity.ChestedHorse"), if (NMS.getMethodHandle(Class.forName("org.bukkit.entity.ChestedHorse"), "setCarryingChest", false,
"setCarryingChest", false, boolean.class); boolean.class) != null) {
SUPPORTS_CARRYING_CHEST = true;
}
} catch (Throwable e) { } catch (Throwable e) {
} }
} }

View File

@ -6,6 +6,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -273,7 +274,7 @@ public class NMS {
public static List<MethodHandle> getFieldsOfType(Class<?> clazz, Class<?> type) { public static List<MethodHandle> getFieldsOfType(Class<?> clazz, Class<?> type) {
List<Field> found = getFieldsMatchingType(clazz, type, false); List<Field> found = getFieldsMatchingType(clazz, type, false);
if (found.isEmpty()) if (found.isEmpty())
return null; return Collections.emptyList();
return found.stream().map(f -> { return found.stream().map(f -> {
try { try {
return LOOKUP.unreflectGetter(f); return LOOKUP.unreflectGetter(f);
@ -585,6 +586,20 @@ public class NMS {
return null; return null;
} }
public static Collection<MethodHandle> getSettersOfType(Class<?> clazz, Class<?> fieldType) {
List<Field> found = getFieldsMatchingType(clazz, fieldType, false);
if (found.isEmpty())
return Collections.emptyList();
return found.stream().map(f -> {
try {
return LOOKUP.unreflectSetter(f);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}).filter(f -> f != null).collect(Collectors.toList());
}
public static String getSoundPath(Sound flag) throws CommandException { public static String getSoundPath(Sound flag) throws CommandException {
return BRIDGE.getSoundPath(flag); return BRIDGE.getSoundPath(flag);
} }
@ -988,7 +1003,6 @@ public class NMS {
private static MethodHandle UNSAFE_PUT_INT; private static MethodHandle UNSAFE_PUT_INT;
private static MethodHandle UNSAFE_PUT_LONG; private static MethodHandle UNSAFE_PUT_LONG;
private static MethodHandle UNSAFE_PUT_OBJECT; private static MethodHandle UNSAFE_PUT_OBJECT;
private static MethodHandle UNSAFE_STATIC_FIELD_OFFSET; private static MethodHandle UNSAFE_STATIC_FIELD_OFFSET;
static { static {

View File

@ -333,7 +333,7 @@ public class Util {
} }
public static String listValuesPretty(Object[] values) { public static String listValuesPretty(Object[] values) {
return "<yellow>" + Joiner.on("<green>, <yellow>").join(values).replace('_', ' ').toLowerCase(Locale.US); return "<yellow>" + Joiner.on("<green>, <yellow>").join(values).replace('_', ' ').toLowerCase(Locale.ROOT);
} }
public static <T extends Enum<?>> T matchEnum(T[] values, String toMatch) { public static <T extends Enum<?>> T matchEnum(T[] values, String toMatch) {
@ -345,7 +345,7 @@ public class Util {
} }
for (T check : values) { for (T check : values) {
String name = check.name().toLowerCase(Locale.US); String name = check.name().toLowerCase(Locale.ROOT);
if (name.replace("_", "").equals(toMatch) || name.startsWith(toMatch)) if (name.replace("_", "").equals(toMatch) || name.startsWith(toMatch))
return check; return check;
@ -422,7 +422,7 @@ public class Util {
} }
public static String prettyEnum(Enum<?> e) { public static String prettyEnum(Enum<?> e) {
return e.name().toLowerCase(Locale.US).replace('_', ' '); return e.name().toLowerCase(Locale.ROOT).replace('_', ' ');
} }
public static String prettyPrintLocation(Location to) { public static String prettyPrintLocation(Location to) {

View File

@ -626,9 +626,19 @@ public class NMSImpl implements NMSBridge {
@Override @Override
public EntityPacketTracker getPacketTracker(org.bukkit.entity.Entity entity) { public EntityPacketTracker getPacketTracker(org.bukkit.entity.Entity entity) {
ServerLevel server = (ServerLevel) getHandle(entity).level(); ServerLevel server = (ServerLevel) getHandle(entity).level();
TrackedEntity entry = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); TrackedEntity tracked = null;
if (entry == null) if (TRACKED_ENTITY_GETTER != null) {
try {
tracked = (TrackedEntity) TRACKED_ENTITY_GETTER.invoke(getHandle(entity));
} catch (Throwable e) {
e.printStackTrace();
}
} else {
tracked = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId());
}
if (tracked == null)
return null; return null;
TrackedEntity entry = tracked;
return new EntityPacketTracker() { return new EntityPacketTracker() {
@Override @Override
public void link(Player player) { public void link(Player player) {
@ -1335,7 +1345,16 @@ public class NMSImpl implements NMSBridge {
@Override @Override
public void replaceTrackerEntry(org.bukkit.entity.Entity entity) { public void replaceTrackerEntry(org.bukkit.entity.Entity entity) {
ServerLevel server = (ServerLevel) getHandle(entity).level(); Entity handle = getHandle(entity);
ServerLevel server = (ServerLevel) handle.level();
for (MethodHandle setter : TRACKED_ENTITY_SETTERS) {
try {
setter.invoke(handle, new CitizensEntityTracker(server.getChunkSource().chunkMap,
(TrackedEntity) TRACKED_ENTITY_GETTER.invoke(handle)));
} catch (Throwable e) {
e.printStackTrace();
}
}
TrackedEntity entry = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId()); TrackedEntity entry = server.getChunkSource().chunkMap.entityMap.get(entity.getEntityId());
if (entry == null) if (entry == null)
return; return;
@ -2520,7 +2539,9 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle ADVANCEMENTS_PLAYER_SETTER = NMS.getFirstFinalSetter(ServerPlayer.class, private static final MethodHandle ADVANCEMENTS_PLAYER_SETTER = NMS.getFirstFinalSetter(ServerPlayer.class,
PlayerAdvancements.class); PlayerAdvancements.class);
private static final MethodHandle ARMADILLO_SCUTE_TIME = NMS.getSetter(Armadillo.class, "cn"); private static final MethodHandle ARMADILLO_SCUTE_TIME = NMS.getSetter(Armadillo.class, "cn");
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP = NMS.getFirstGetter(AttributeSupplier.class, Map.class); private static final MethodHandle ATTRIBUTE_PROVIDER_MAP = NMS.getFirstGetter(AttributeSupplier.class, Map.class);
private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFirstFinalSetter(AttributeSupplier.class, private static final MethodHandle ATTRIBUTE_PROVIDER_MAP_SETTER = NMS.getFirstFinalSetter(AttributeSupplier.class,
Map.class); Map.class);
@ -2572,10 +2593,10 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle NAVIGATION_CREATE_PATHFINDER = NMS private static final MethodHandle NAVIGATION_CREATE_PATHFINDER = NMS
.getFirstMethodHandleWithReturnType(PathNavigation.class, true, PathFinder.class, int.class); .getFirstMethodHandleWithReturnType(PathNavigation.class, true, PathFinder.class, int.class);
private static final MethodHandle NAVIGATION_PATH = NMS.getFirstGetter(PathNavigation.class, Path.class); private static final MethodHandle NAVIGATION_PATH = NMS.getFirstGetter(PathNavigation.class, Path.class);
private static final MethodHandle NAVIGATION_PATHFINDER = NMS.getFirstFinalSetter(PathNavigation.class, private static final MethodHandle NAVIGATION_PATHFINDER = NMS.getFirstFinalSetter(PathNavigation.class,
PathFinder.class); PathFinder.class);
private static final MethodHandle NAVIGATION_WORLD_FIELD = NMS.getFirstSetter(PathNavigation.class, Level.class); private static final MethodHandle NAVIGATION_WORLD_FIELD = NMS.getFirstSetter(PathNavigation.class, Level.class);
private static final MethodHandle PLAYER_INFO_ENTRIES_LIST = NMS private static final MethodHandle PLAYER_INFO_ENTRIES_LIST = NMS
.getFirstFinalSetter(ClientboundPlayerInfoUpdatePacket.class, List.class); .getFirstFinalSetter(ClientboundPlayerInfoUpdatePacket.class, List.class);
private static final MethodHandle PLAYERINFO_ENTRIES = PLAYER_INFO_ENTRIES_LIST; private static final MethodHandle PLAYERINFO_ENTRIES = PLAYER_INFO_ENTRIES_LIST;
@ -2592,6 +2613,9 @@ public class NMSImpl implements NMSBridge {
private static final MethodHandle SIZE_FIELD_SETTER = NMS.getFirstSetter(Entity.class, EntityDimensions.class); private static final MethodHandle SIZE_FIELD_SETTER = NMS.getFirstSetter(Entity.class, EntityDimensions.class);
private static MethodHandle SKULL_META_PROFILE; private static MethodHandle SKULL_META_PROFILE;
private static MethodHandle TEAM_FIELD; private static MethodHandle TEAM_FIELD;
private static final MethodHandle TRACKED_ENTITY_GETTER = NMS.getFirstGetter(Entity.class, TrackedEntity.class);
private static final Collection<MethodHandle> TRACKED_ENTITY_SETTERS = NMS.getSettersOfType(Entity.class,
TrackedEntity.class);
static { static {
try { try {
ENTITY_REGISTRY = new CustomEntityRegistry(BuiltInRegistries.ENTITY_TYPE); ENTITY_REGISTRY = new CustomEntityRegistry(BuiltInRegistries.ENTITY_TYPE);