Use EntitiesLoadEvent instead of ChunkLoadEvent, other minor fixes

This commit is contained in:
fullwall 2022-08-04 11:48:48 +08:00
parent 3409b06b93
commit 0afeb4792d
12 changed files with 170 additions and 45 deletions

View File

@ -47,8 +47,10 @@ import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.vehicle.VehicleDamageEvent;
import org.bukkit.event.vehicle.VehicleDestroyEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.world.ChunkEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.EntitiesLoadEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.inventory.meta.SkullMeta;
@ -151,8 +153,7 @@ public class EventListen implements Listener {
Predicates.notNull());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onChunkLoad(ChunkLoadEvent event) {
private void loadNPCs(ChunkEvent event) {
ChunkCoord coord = new ChunkCoord(event.getChunk());
Runnable runnable = new Runnable() {
@Override
@ -170,6 +171,14 @@ public class EventListen implements Listener {
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onChunkLoad(ChunkLoadEvent event) {
if (usingEntitiesLoadEvents())
return;
loadNPCs(event);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onChunkUnload(final ChunkUnloadEvent event) {
final List<NPC> toDespawn = Lists.newArrayList();
@ -232,6 +241,11 @@ public class EventListen implements Listener {
checkCreationEvent(event);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntitiesLoad(EntitiesLoadEvent event) {
loadNPCs(event);
}
/*
* Entity events
*/
@ -732,4 +746,18 @@ public class EventListen implements Listener {
}
return npc.spawn(spawn, SpawnReason.CHUNK_LOAD);
}
private static boolean usingEntitiesLoadEvents() {
if (USING_ENTITIES_LOAD == null) {
try {
Class.forName("org.bukkit.event.world.EntitiesLoadEvent");
USING_ENTITIES_LOAD = true;
} catch (ClassNotFoundException swallow) {
USING_ENTITIES_LOAD = false;
}
}
return USING_ENTITIES_LOAD;
}
private static Boolean USING_ENTITIES_LOAD;
}

View File

@ -110,7 +110,7 @@ public class Settings {
DISABLE_LOOKCLOSE_WHILE_NAVIGATING("npc.default.look-close.disable-while-navigating", true),
DISABLE_MC_NAVIGATION_FALLBACK("npc.pathfinding.disable-mc-fallback-navigation", true),
DISABLE_TABLIST("npc.tablist.disable", true),
ENTITY_SPAWN_WAIT_TICKS("general.entity-spawn-wait-ticks", 10),
ENTITY_SPAWN_WAIT_TICKS("general.entity-spawn-wait-ticks", 20),
ERROR_COLOUR("general.color-scheme.message-error", "<c>"),
FOLLOW_ACROSS_WORLDS("npc.follow.teleport-across-worlds", true),
HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "<e>"),

View File

@ -62,9 +62,12 @@ public class EditorCommands {
desc = "Toggle the text editor",
modifiers = { "text" },
min = 1,
max = 1,
permission = "citizens.npc.edit.text")
public void text(CommandContext args, Player player, NPC npc) {
if (player.isConversing() && Editor.hasEditor(player) && args.argsLength() > 1) {
player.acceptConversationInput(args.getJoinedStrings(1));
return;
}
Editor.enterOrLeave(player, npc.getOrAddTrait(Text.class).getEditor(player));
}
}

View File

@ -1580,8 +1580,8 @@ public class NPCCommands {
permission = "citizens.npc.passive")
public void passive(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
boolean passive = args.hasValueFlag("set") ? Boolean.parseBoolean(args.getFlag("set"))
: npc.data().get(NPC.DAMAGE_OTHERS_METADATA, true);
npc.data().setPersistent(NPC.DAMAGE_OTHERS_METADATA, !passive);
: !npc.data().get(NPC.DAMAGE_OTHERS_METADATA, true);
npc.data().setPersistent(NPC.DAMAGE_OTHERS_METADATA, passive);
Messaging.sendTr(sender, passive ? Messages.PASSIVE_SET : Messages.PASSIVE_UNSET, npc.getName());
}

View File

@ -3,6 +3,7 @@ package net.citizensnpcs.npc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
import java.util.function.Consumer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -19,6 +20,7 @@ import org.bukkit.scheduler.BukkitRunnable;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.SetMultimap;
import net.citizensnpcs.NPCNeedsRespawnEvent;
@ -292,22 +294,23 @@ public class CitizensNPC extends AbstractNPC {
NMS.setBodyYaw(getEntity(), at.getYaw());
final Location to = at;
BukkitRunnable postSpawn = new BukkitRunnable() {
Consumer<Runnable> postSpawn = new Consumer<Runnable>() {
private int timer;
@Override
public void run() {
if (timer++ > Setting.ENTITY_SPAWN_WAIT_TICKS.asInt()) {
Messaging.debug("Couldn't spawn", CitizensNPC.this, "entity not added to world");
entityController.remove();
cancel();
Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(CitizensNPC.this, to));
public void accept(Runnable cancel) {
if (getEntity() == null || !getEntity().isValid()) {
if (timer++ > Setting.ENTITY_SPAWN_WAIT_TICKS.asInt()) {
Messaging.debug("Couldn't spawn ", CitizensNPC.this, "waited", timer,
"ticks but entity not added to world");
entityController.remove();
cancel.run();
Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(CitizensNPC.this, to));
}
return;
}
if (getEntity() == null || !getEntity().isValid())
return;
// Set the spawned state
getOrAddTrait(CurrentLocation.class).setLocation(to);
getOrAddTrait(Spawned.class).setSpawned(true);
@ -319,14 +322,13 @@ public class CitizensNPC extends AbstractNPC {
Messaging.debug("Couldn't spawn", CitizensNPC.this, "SpawnReason." + reason,
"due to event cancellation.");
entityController.remove();
cancel();
cancel.run();
return;
}
navigator.onSpawn();
Collection<Trait> onSpawn = traits.values();
for (Trait trait : onSpawn.toArray(new Trait[onSpawn.size()])) {
for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) {
try {
trait.onSpawn();
} catch (Throwable ex) {
@ -335,17 +337,20 @@ public class CitizensNPC extends AbstractNPC {
}
}
if (getEntity() instanceof LivingEntity) {
EntityType type = getEntity().getType();
if (type.isAlive()) {
LivingEntity entity = (LivingEntity) getEntity();
entity.setRemoveWhenFarAway(false);
if (NMS.getStepHeight(entity) < 1) {
NMS.setStepHeight(entity, 1);
}
if (getEntity() instanceof Player) {
if (type == EntityType.PLAYER) {
NMS.replaceTrackerEntry((Player) getEntity());
PlayerUpdateTask.registerPlayer(getEntity());
}
if (SUPPORT_NODAMAGE_TICKS && (Setting.DEFAULT_SPAWN_NODAMAGE_TICKS.asInt() != 20
|| data().has(NPC.Metadata.SPAWN_NODAMAGE_TICKS))) {
try {
@ -366,13 +371,19 @@ public class CitizensNPC extends AbstractNPC {
updateCustomName();
Messaging.debug("Spawned", CitizensNPC.this, "SpawnReason." + reason);
cancel();
cancel.run();
}
};
if (isSpawned()) {
postSpawn.runTask(CitizensAPI.getPlugin());
if (getEntity() != null && getEntity().isValid()) {
postSpawn.accept(() -> {
});
} else {
postSpawn.runTaskTimer(CitizensAPI.getPlugin(), 0, 1);
new BukkitRunnable() {
@Override
public void run() {
postSpawn.accept(() -> cancel());
}
}.runTaskTimer(CitizensAPI.getPlugin(), 0, 1);
}
return true;

View File

@ -171,8 +171,6 @@ public class SkinPacketTracker {
* The radius.
*/
public void updateNearbyViewers(double radius) {
radius *= radius;
org.bukkit.World world = entity.getBukkitEntity().getWorld();
Player from = entity.getBukkitEntity();
Location location = from.getLocation();
@ -181,12 +179,12 @@ public class SkinPacketTracker {
if (player == null || player.hasMetadata("NPC"))
continue;
player.getLocation(CACHE_LOCATION);
if (!player.canSee(from) || !location.getWorld().equals(CACHE_LOCATION.getWorld()))
if (!location.getWorld().equals(player.getLocation(CACHE_LOCATION).getWorld()) || !player.canSee(from))
continue;
if (location.distanceSquared(CACHE_LOCATION) > radius)
if (location.distance(CACHE_LOCATION) > radius)
continue;
updateViewer(player);
}
}

View File

@ -74,10 +74,7 @@ public class SkinUpdateTracker {
Location playerLoc = player.getLocation(CACHE_LOCATION);
Location skinLoc = entity.getLocation(NPC_LOCATION);
double viewDistance = Setting.NPC_SKIN_VIEW_DISTANCE.asDouble();
viewDistance *= viewDistance;
if (playerLoc.distanceSquared(skinLoc) > viewDistance)
if (playerLoc.distance(skinLoc) > Setting.NPC_SKIN_VIEW_DISTANCE.asDouble())
return false;
// see if the NPC is within the players field of view

View File

@ -66,7 +66,7 @@ public class SmoothRotationTrait extends Trait {
* The target location to face
*/
public void rotateToFace(Location target) {
this.globalSession.setTarget(target);
globalSession.setTarget(target);
}
public void rotateToHave(float yaw, float pitch) {
@ -119,13 +119,22 @@ public class SmoothRotationTrait extends Trait {
}
}
public static class RotationParams implements Persistable {
public static class RotationParams implements Persistable, Cloneable {
private boolean headOnly = false;
private boolean immediate = false;
private float maxPitchPerTick = 10;
private float maxYawPerTick = 40;
private final float[] pitchRange = { 0, 0 };
private final float[] yawRange = { 0, 0 };
private float[] pitchRange = { -180, 180 };
private float[] yawRange = { -180, 180 };
@Override
public RotationParams clone() {
try {
return (RotationParams) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
public RotationParams headOnly(boolean headOnly) {
this.headOnly = headOnly;
@ -151,6 +160,14 @@ public class SmoothRotationTrait extends Trait {
if (key.keyExists("maxYawPerTick")) {
maxYawPerTick = (float) key.getDouble("maxYawPerTick");
}
if (key.keyExists("yawRange")) {
String[] parts = key.getString("yawRange").split(",");
yawRange = new float[] { Float.parseFloat(parts[0]), Float.parseFloat(parts[1]) };
}
if (key.keyExists("pitchRange")) {
String[] parts = key.getString("pitchRange").split(",");
pitchRange = new float[] { Float.parseFloat(parts[0]), Float.parseFloat(parts[1]) };
}
}
public RotationParams maxPitchPerTick(float val) {
@ -163,13 +180,22 @@ public class SmoothRotationTrait extends Trait {
return this;
}
public RotationParams pitchRange(float[] val) {
this.pitchRange = val;
return this;
}
public float rotateHeadYawTowards(int t, float yaw, float targetYaw) {
return rotateTowards(yaw, targetYaw, maxYawPerTick);
float out = rotateTowards(yaw, targetYaw, maxYawPerTick);
return clamp(out, yawRange[0], yawRange[1]);
}
public float rotatePitchTowards(int t, float pitch, float targetPitch) {
return rotateTowards(pitch, targetPitch, maxPitchPerTick);
}/*
float out = rotateTowards(pitch, targetPitch, maxPitchPerTick);
return clamp(out, pitchRange[0], pitchRange[1]);
}
/*
* public Vector3 SuperSmoothVector3Lerp( Vector3 pastPosition, Vector3 pastTargetPosition, Vector3 targetPosition, float time, float speed ){
Vector3 f = pastPosition - pastTargetPosition + (targetPosition - pastTargetPosition) / (speed * time);
return targetPosition - (targetPosition - pastTargetPosition) / (speed*time) + f * Mathf.Exp(-speed*time);
@ -189,12 +215,35 @@ public class SmoothRotationTrait extends Trait {
if (immediate) {
key.setBoolean("immediate", immediate);
}
if (maxPitchPerTick != 10) {
key.setDouble("maxPitchPerTick", maxPitchPerTick);
} else {
key.removeKey("maxPitchPerTick");
}
if (maxYawPerTick != 40) {
key.setDouble("maxYawPerTick", maxYawPerTick);
} else {
key.removeKey("maxYawPerTick");
}
if (pitchRange[0] != -180 || pitchRange[1] != 180) {
key.setString("pitchRange", pitchRange[0] + "," + pitchRange[1]);
} else {
key.removeKey("pitchRange");
}
if (yawRange[0] != -180 || yawRange[1] != 180) {
key.setString("yawRange", yawRange[0] + "," + yawRange[1]);
} else {
key.removeKey("yawRange");
}
}
public RotationParams yawRange(float[] val) {
this.yawRange = val;
return this;
}
}
@ -207,7 +256,7 @@ public class SmoothRotationTrait extends Trait {
this.params = params;
}
private float getTargetPitch() {
public float getTargetPitch() {
double dx = tx - getX();
double dy = ty - (getY() + getEyeY());
double dz = tz - getZ();
@ -215,10 +264,22 @@ public class SmoothRotationTrait extends Trait {
return (float) -Math.toDegrees(Math.atan2(dy, diag));
}
private float getTargetYaw() {
public double getTargetX() {
return tx;
}
public double getTargetY() {
return ty;
}
public float getTargetYaw() {
return (float) Math.toDegrees(Math.atan2(tz - getZ(), tx - getX())) - 90.0F;
}
public double getTargetZ() {
return tz;
}
public boolean hasTarget() {
return t >= 0;
}

View File

@ -26,6 +26,7 @@ public class TextBasePrompt extends StringPrompt {
public Prompt acceptInput(ConversationContext context, String original) {
String[] parts = ChatColor.stripColor(original.trim()).split(" ");
String input = parts[0];
CommandSender sender = (CommandSender) context.getForWhom();
if (input.equalsIgnoreCase("add")) {
text.add(Joiner.on(' ').join(Arrays.copyOfRange(parts, 1, parts.length)));

View File

@ -26,7 +26,9 @@ import org.bukkit.util.Vector;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import io.netty.util.Version;
import net.citizensnpcs.api.event.NPCCollisionEvent;
import net.citizensnpcs.api.event.NPCPushEvent;
import net.citizensnpcs.api.npc.NPC;
@ -326,6 +328,27 @@ public class Util {
to.getBlockZ(), (int) to.getYaw(), (int) to.getPitch());
}
public static boolean requiresNettyChannelMetadata() {
if (REQUIRES_CHANNEL_METADATA != null)
return REQUIRES_CHANNEL_METADATA;
Version version = Version.identify().get("netty-common");
if (version == null) {
version = Version.identify().get("netty-all");
}
if (version == null)
return REQUIRES_CHANNEL_METADATA = false;
try {
Integer[] parts = Iterables.toArray(
Iterables.transform(Splitter.on('.').split(version.artifactVersion()), s -> Integer.parseInt(s)),
int.class);
return REQUIRES_CHANNEL_METADATA = parts[0] > 4 || (parts[0] == 4 && parts[1] > 1);
} catch (Throwable t) {
t.printStackTrace();
return REQUIRES_CHANNEL_METADATA = true;
}
}
/**
* Sets the entity's yaw and pitch directly including head yaw.
*/
@ -334,6 +357,8 @@ public class Util {
}
private static final Location AT_LOCATION = new Location(null, 0, 0, 0);
private static final Scoreboard DUMMY_SCOREBOARD = Bukkit.getScoreboardManager().getNewScoreboard();
private static String MINECRAFT_REVISION;
private static Boolean REQUIRES_CHANNEL_METADATA;
}

View File

@ -339,14 +339,14 @@ citizens.editors.text.range-set=[[Range]] set to [[{0}]].
citizens.editors.text.delay-set=[[Delay]] set to [[{0}]] seconds.
citizens.editors.text.realistic-looking-set=[[Realistic looking]] set to [[{0}]].
citizens.editors.text.speech-bubbles-set=[[Speech bubbles]] set to [[{0}]].
citizens.editors.text.start-prompt=<<[[add:command(add ):Add text>> | <<[[item:suggest(item ):Set the talk item in hand pattern (set to ''default'' to clear)>> | <<[[range:suggest(range ):Set the talking range in blocks>> | <<[[delay:suggest(delay ):Set the talking delay in seconds>><br><<{0}talk close:command(close):Toggle sending messages when players get close>> | <<{1}random:command(random):Toggle random talking>> | <<{2}speech bubbles:command(speech bubbles):Toggle showing text as holograms instead of messages>> | <<{3}realistic:command(realistic looking):Toggle requiring line of sight before speaking>>
citizens.editors.text.start-prompt=<<[[add:suggest(add ):Add text>> | <<[[item:suggest(item ):Set the talk item in hand pattern (set to ''default'' to clear)>> | <<[[range:suggest(range ):Set the talking range in blocks>> | <<[[delay:suggest(delay ):Set the talking delay in seconds>><br><<{0}talk close:command(/npc text close):Toggle sending messages when players get close>> | <<{1}random:command(/npc text random):Toggle random talking>> | <<{2}speech bubbles:command(/npc text speech bubbles):Toggle showing text as holograms instead of messages>> | <<{3}realistic:command(/npc text realistic looking):Toggle requiring line of sight before speaking>>
citizens.editors.text.talk-item-set=[[Talk item pattern]] set to [[{0}]].
citizens.editors.text.text-list-header=Current text:
citizens.editors.waypoints.wander.editing-regions-stop=Exited the region editor.
citizens.editors.waypoints.wander.worldguard-region-not-found=WorldGuard region not found.
citizens.editors.waypoints.wander.worldguard-region-set=WorldGuard region set to [[{0}]].
citizens.editors.waypoints.wander.range-set=Wander range set to xrange [[{0}]] and yrange [[{1}]].
citizens.editors.waypoints.wander.begin=<b>Entered the wander waypoint editor.<br><<x range:suggest(xrange ):Set the x range in blocks>> | <<y range:suggest(yrange ):Set the y range in blocks>> | <<delay:suggest(delay ):Ticks to wait in between wanders>><br><<Enter the region editor:command(regions)>> | <<Restrict wandering to WorldGuard regions:suggest(worldguardregion )>>
citizens.editors.waypoints.wander.begin=<b>Entered the wander waypoint editor.<br><<x range:suggest(xrange ):Set the x range in blocks>> | <<y range:suggest(yrange ):Set the y range in blocks>> | <<delay:suggest(delay ):Ticks to wait in between wanders>><br><<Enter the region editor:suggest(regions)>> | <<Restrict wandering to WorldGuard regions:suggest(worldguardregion )>>
citizens.editors.waypoints.wander.end=Exited the wander waypoint editor.
citizens.editors.waypoints.wander.delay-set=Delay between wanders set to [[{0}]] ticks.
citizens.editors.waypoints.wander.invalid-delay=Invalid delay specified.

View File

@ -953,6 +953,7 @@ public class NMSImpl implements NMSBridge {
@Override
public void look(org.bukkit.entity.Entity entity, Location to, boolean headOnly, boolean immediate) {
Entity handle = NMSImpl.getHandle(entity);
if (immediate || headOnly || BAD_CONTROLLER_LOOK.contains(handle.getBukkitEntity().getType())
|| (!(handle instanceof Mob) && !(handle instanceof EntityHumanNPC))) {
Location fromLocation = entity.getLocation(FROM_LOCATION);