Fix an issue with setDestination not working immediately after spawn

This commit is contained in:
fullwall 2012-05-23 16:51:33 +08:00
parent 08e5f8223d
commit 22602c225a
10 changed files with 83 additions and 48 deletions

View File

@ -4,7 +4,6 @@ import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.logging.Level;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
@ -152,7 +151,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
getServer().getScheduler().cancelTasks(this);
}
Messaging.log("v" + getDescription().getVersion() + " disabled.");
Messaging.logF("v%s disabled.", getDescription().getVersion());
}
@Override
@ -161,8 +160,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
String mcVersion = ((CraftServer) getServer()).getServer().getVersion();
compatible = mcVersion.startsWith(COMPATIBLE_MC_VERSION);
if (!compatible) {
Messaging.log(Level.SEVERE, "v" + getDescription().getVersion() + " is not compatible with Minecraft v"
+ mcVersion + ". Disabling.");
Messaging.severeF("v%s is not compatible with Minecraft v%s. Disabling.", getDescription().getVersion(),
mcVersion);
getServer().getPluginManager().disablePlugin(this);
return;
}
@ -181,7 +180,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
registerCommands();
Messaging.log("v" + getDescription().getVersion() + " enabled.");
Messaging.logF("v%s enabled.", getDescription().getVersion());
// Setup NPCs after all plugins have been enabled (allows for multiworld
// support and for NPCs to properly register external settings)
@ -193,7 +192,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
startMetrics();
}
}) == -1) {
Messaging.log(Level.SEVERE, "Issue enabling plugin. Disabling.");
Messaging.severe("Issue enabling plugin. Disabling.");
getServer().getPluginManager().disablePlugin(this);
}
}
@ -254,7 +253,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
for (DataKey key : saves.getKey("npc").getIntegerSubKeys()) {
int id = Integer.parseInt(key.name());
if (!key.keyExists("name")) {
Messaging.log("Could not find a name for the NPC with ID '" + id + "'.");
Messaging.logF("Could not find a name for the NPC with ID '%s'.", id);
continue;
}
String unparsedEntityType = key.getString("traits.type", "PLAYER");
@ -263,8 +262,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
try {
type = EntityType.valueOf(unparsedEntityType);
} catch (IllegalArgumentException ex) {
Messaging.log("NPC type '" + unparsedEntityType
+ "' was not recognized. Did you spell it correctly?");
Messaging.logF("NPC type '%s' was not recognized. Did you spell it correctly?", unparsedEntityType);
continue;
}
}
@ -275,7 +273,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
if (npc.isSpawned())
++spawned;
}
Messaging.log("Loaded " + created + " NPCs (" + spawned + " spawned).");
Messaging.logF("Loaded %d NPCs (%d spawned).", created, spawned);
}
private void setupScripting() {
@ -302,7 +300,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
saves = new YamlStorage(getDataFolder() + File.separator + Setting.STORAGE_FILE.asString(),
"Citizens NPC Storage");
}
Messaging.log("Save method set to", saves.toString());
Messaging.logF("Save method set to %s.", saves.toString());
}
private void startMetrics() {

View File

@ -19,7 +19,7 @@ public class Settings {
config.load();
for (Setting setting : Setting.values()) {
if (!root.keyExists(setting.path)) {
Messaging.log("Writing default setting: '" + setting.path + "'");
Messaging.logF("Writing default setting: '%s'", setting.path);
root.setRaw(setting.path, setting.value);
} else
setting.set(root.getRaw(setting.path));

View File

@ -137,7 +137,7 @@ public class NPCCommands {
max = 5,
permission = "npc.create")
@Requirements
public void create(CommandContext args, Player player, NPC npc) {
public void create(CommandContext args, final Player player, NPC npc) {
String name = args.getString(1);
if (name.length() > 16) {
Messaging.sendError(player, "NPC names cannot be longer than 16 characters. The name has been shortened.");

View File

@ -5,12 +5,14 @@ import java.lang.reflect.Field;
import java.util.Map;
import net.citizensnpcs.api.npc.NPC;
import net.minecraft.server.Block;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.EntityTypes;
import net.minecraft.server.World;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
@SuppressWarnings("unchecked")
@ -41,6 +43,16 @@ public abstract class CitizensMobNPC extends CitizensNPC {
protected EntityLiving createHandle(Location loc) {
EntityLiving entity = createEntityFromClass(((CraftWorld) loc.getWorld()).getHandle());
entity.setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
// entity.onGround isn't updated right away - we approximate here so
// that things like pathfinding still work *immediately* after spawn.
org.bukkit.Material beneath = loc.getBlock().getRelative(BlockFace.DOWN).getType();
if (beneath.isBlock()) {
Block block = Block.byId[beneath.getId()];
if (block != null && block.material != null) {
entity.onGround = block.material.isSolid();
}
}
return entity;
}

View File

@ -113,17 +113,15 @@ public abstract class CitizensNPC extends AbstractNPC {
for (DataKey traitKey : root.getRelative("traits").getSubKeys()) {
Trait trait = traitManager.getTrait(traitKey.name(), this);
if (trait == null) {
Messaging.severe(String.format(
"Skipped missing trait '%s' while loading NPC ID: '%d'. Has the name changed?",
traitKey.name(), getId()));
Messaging.severeF("Skipped missing trait '%s' while loading NPC ID: '%d'. Has the name changed?",
traitKey.name(), getId());
continue;
}
addTrait(trait);
try {
getTrait(trait.getClass()).load(traitKey);
} catch (NPCLoadException ex) {
Messaging.log(
String.format("The trait '%s' failed to load for NPC ID: '%d'.", traitKey.name(), getId()),
Messaging.logF("The trait '%s' failed to load for NPC ID: '%d'.", traitKey.name(), getId(),
ex.getMessage());
}
}
@ -195,7 +193,7 @@ public abstract class CitizensNPC extends AbstractNPC {
super.update();
ai.update();
} catch (Exception ex) {
Messaging.log("Exception while updating " + getId() + ": " + ex.getMessage() + ".");
Messaging.logF("Exception while updating %d: %s.", getId(), ex.getMessage());
ex.printStackTrace();
}
}

View File

@ -1,6 +1,7 @@
package net.citizensnpcs.npc.ai;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import net.citizensnpcs.api.ai.AI;
@ -98,13 +99,12 @@ public class CitizensAI implements AI {
throw new IllegalArgumentException("destination cannot be null");
if (!npc.isSpawned())
throw new IllegalStateException("npc is not spawned");
if (destination.getWorld() != npc.getBukkitEntity().getWorld())
throw new IllegalArgumentException("location is not in the same world");
boolean replaced = executing != null;
executing = new MCNavigationStrategy(npc, destination);
if (!replaced)
return;
for (int i = 0; i < callbacks.size(); ++i) {
NavigationCallback next = callbacks.get(i).get();
if (next == null || (replaced && next.onCancel(this, CancelReason.REPLACE)) || next.onBegin(this)) {
@ -121,8 +121,6 @@ public class CitizensAI implements AI {
boolean replaced = executing != null;
executing = new MCTargetStrategy(npc, target, aggressive);
if (!replaced)
return;
for (int i = 0; i < callbacks.size(); ++i) {
NavigationCallback next = callbacks.get(i).get();
if (next == null || (replaced && next.onCancel(this, CancelReason.REPLACE)) || next.onBegin(this)) {
@ -170,17 +168,19 @@ public class CitizensAI implements AI {
if (toRemove == null)
return;
for (Goal goal : toRemove) {
for (int i = 0; i < executingGoals.size(); ++i) {
GoalEntry entry = executingGoals.get(i);
Iterator<GoalEntry> itr = executingGoals.iterator();
while (itr.hasNext()) {
GoalEntry entry = itr.next();
if (entry.getGoal().equals(goal)) {
entry.getGoal().reset();
executingGoals.remove(i);
itr.remove();
}
}
for (int i = 0; i < goals.size(); ++i) {
GoalEntry entry = goals.get(i);
itr = goals.iterator();
while (itr.hasNext()) {
GoalEntry entry = itr.next();
if (entry.getGoal().equals(goal))
goals.remove(i);
itr.remove();
}
}

View File

@ -4,7 +4,6 @@ import java.lang.reflect.Field;
import java.util.Map;
import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.npc.entity.EntityHumanNPC;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.Navigation;
@ -18,7 +17,7 @@ public class MCNavigationStrategy implements PathStrategy {
private final EntityLiving entity;
private final Navigation navigation;
MCNavigationStrategy(CitizensNPC npc, Location dest) {
MCNavigationStrategy(final CitizensNPC npc, final Location dest) {
entity = npc.getHandle();
if (npc.getBukkitEntity() instanceof Player) {
entity.onGround = true;
@ -26,14 +25,14 @@ public class MCNavigationStrategy implements PathStrategy {
// navigation won't execute, and calling entity.move doesn't
// entirely fix the problem.
}
navigation = npc.getHandle().al();
navigation = entity.al();
navigation.a(dest.getX(), dest.getY(), dest.getZ(), getSpeed(npc.getHandle()));
}
MCNavigationStrategy(EntityLiving entity, EntityLiving target) {
this.entity = entity;
if (entity instanceof EntityHumanNPC) {
if (entity.getBukkitEntity() instanceof Player) {
entity.onGround = true; // see above
}
navigation = entity.al();
@ -60,10 +59,6 @@ public class MCNavigationStrategy implements PathStrategy {
@Override
public boolean update() {
if (entity instanceof EntityHumanNPC) {
navigation.d();
((EntityHumanNPC) entity).moveOnCurrentHeading();
}
return navigation.e();
}

View File

@ -28,13 +28,13 @@ public class CitizensHumanNPC extends CitizensNPC implements Equipable {
WorldServer ws = ((CraftWorld) loc.getWorld()).getHandle();
final EntityHumanNPC handle = new EntityHumanNPC(ws.getServer().getServer(), ws,
StringHelper.parseColors(getFullName()), new ItemInWorldManager(ws), this);
handle.getBukkitEntity().teleport(loc);
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
@Override
public void run() {
handle.X = loc.getYaw() % 360;
handle.getBukkitEntity().teleport(loc);
// set the position in another tick - if done immediately,
// minecraft will not update the player's position.
// set the head yaw in another tick - if done immediately,
// minecraft will not update it.
}
});
return handle;
@ -89,7 +89,7 @@ public class CitizensHumanNPC extends CitizensNPC implements Equipable {
trait.set(i, null);
}
}
Messaging.send(equipper, "<e>" + getName() + " <a>had all of its items removed.");
Messaging.sendF(equipper, "<e>%s<a>had all of its items removed.", getName());
}
// Drop any previous equipment on the ground
if (trait.get(slot) != null && trait.get(slot).getType() != Material.AIR)

View File

@ -11,6 +11,7 @@ import net.citizensnpcs.npc.network.NPCSocket;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ItemInWorldManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Navigation;
import net.minecraft.server.NetHandler;
import net.minecraft.server.NetworkManager;
import net.minecraft.server.World;
@ -44,7 +45,11 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHandle {
@Override
public void F_() {
super.F_();
if (!npc.getAI().hasDestination() && (motX != 0 || motZ != 0 || motY != 0)) {
Navigation navigation = al();
if (!navigation.e()) {
navigation.d();
moveOnCurrentHeading();
} else if (motX != 0 || motZ != 0 || motY != 0) {
a(0, 0);
}
if (noDamageTicks > 0)
@ -52,7 +57,7 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHandle {
npc.update();
}
public void moveOnCurrentHeading() {
private void moveOnCurrentHeading() {
getControllerMove().c();
getControllerLook().a();
getControllerJump().b();

View File

@ -1,5 +1,6 @@
package net.citizensnpcs.util;
import java.util.Arrays;
import java.util.logging.Level;
import net.citizensnpcs.Settings.Setting;
@ -29,12 +30,34 @@ public class Messaging {
log(Level.INFO, msg);
}
public static void send(CommandSender sender, Object msg) {
sender.sendMessage(StringHelper.parseColors(msg.toString()));
private static String getFormatted(Object[] msg) {
String toFormat = msg[0].toString();
Object[] args = msg.length > 1 ? Arrays.copyOfRange(msg, 1, msg.length) : new Object[] {};
return String.format(toFormat, args);
}
public static void sendError(CommandSender sender, Object msg) {
send(sender, ChatColor.RED.toString() + msg);
public static void logF(Object... msg) {
log(getFormatted(msg));
}
public static void send(CommandSender sender, Object... msg) {
String joined = SPACE.join(msg);
joined = StringHelper.parseColors(joined);
sender.sendMessage(joined);
}
public static void sendF(CommandSender sender, Object... msg) {
String joined = getFormatted(msg);
joined = StringHelper.parseColors(joined);
sender.sendMessage(joined);
}
public static void sendError(CommandSender sender, Object... msg) {
send(sender, ChatColor.RED.toString() + SPACE.join(msg));
}
public static void sendErrorF(CommandSender sender, Object... msg) {
sendF(sender, ChatColor.RED.toString() + SPACE.join(msg));
}
public static void sendWithNPC(CommandSender sender, Object msg, NPC npc) {
@ -55,4 +78,8 @@ public class Messaging {
public static void severe(Object... messages) {
log(Level.SEVERE, messages);
}
public static void severeF(Object... messages) {
log(Level.SEVERE, getFormatted(messages));
}
}