diff --git a/pom.xml b/pom.xml index dfdcbdf3..2e2c9631 100644 --- a/pom.xml +++ b/pom.xml @@ -1,43 +1,23 @@ 4.0.0 - org.bukkit - bukkit + + + org.spigotmc + spigot-parent + dev-SNAPSHOT + ../pom.xml + + + org.spigotmc + spigot-api 1.6.4-R0.1-SNAPSHOT - Bukkit - http://www.bukkit.org + Spigot-API + http://www.spigotmc.org UTF-8 - - scm:git:git@github.com:Bukkit/Bukkit.git - scm:git:git://github.com/Bukkit/Bukkit.git - https://github.com/Bukkit/Bukkit/tree/master/ - - - - jenkins - http://ci.bukkit.org - - - - - jd.bukkit.org - file:///home/javadocs/public_html/ - - - repobo-rel - repo.bukkit.org Releases - http://repo.bukkit.org/content/repositories/releases/ - - - repobo-snap - repo.bukkit.org Snapshots - http://repo.bukkit.org/content/repositories/snapshots/ - - - @@ -98,21 +78,21 @@ org.yaml snakeyaml - 1.9 + 1.12 jar compile com.googlecode.json-simple json-simple - 1.1 + 1.1.1 jar compile org.avaje ebean - 2.7.3 + 2.8.1 jar compile @@ -126,7 +106,7 @@ commons-lang commons-lang - 2.3 + 2.6 diff --git a/src/main/java/org/bukkit/Effect.java b/src/main/java/org/bukkit/Effect.java index 708bee92..62c7c114 100644 --- a/src/main/java/org/bukkit/Effect.java +++ b/src/main/java/org/bukkit/Effect.java @@ -5,6 +5,7 @@ import com.google.common.collect.Maps; import org.bukkit.block.BlockFace; +import org.bukkit.material.MaterialData; import org.bukkit.potion.Potion; /** @@ -78,27 +79,178 @@ public enum Effect { /** * The flames seen on a mobspawner; a visual effect. */ - MOBSPAWNER_FLAMES(2004, Type.VISUAL); + MOBSPAWNER_FLAMES(2004, Type.VISUAL), + /** + * The spark that comes off a fireworks + */ + FIREWORKS_SPARK("fireworksSpark", Type.PARTICLE), + /** + * Critical hit particles + */ + CRIT("crit", Type.PARTICLE), + /** + * Blue critical hit particles + */ + MAGIC_CRIT("magicCrit", Type.PARTICLE), + /** + * Multicolored potion effect particles + */ + POTION_SWIRL("mobSpell", Type.PARTICLE), + /** + * Multicolored potion effect particles that are slightly transparent + */ + POTION_SWIRL_TRANSPARENT("mobSpellAmbient", Type.PARTICLE), + /** + * A puff of white potion swirls + */ + SPELL("spell", Type.PARTICLE), + /** + * A puff of white stars + */ + INSTANT_SPELL("instantSpell", Type.PARTICLE), + /** + * A puff of purple particles + */ + WITCH_MAGIC("witchMagic", Type.PARTICLE), + /** + * The note that appears above note blocks + */ + NOTE("note", Type.PARTICLE), + /** + * The particles shown at nether portals + */ + PORTAL("portal", Type.PARTICLE), + /** + * The symbols that fly towards the enchantment table + */ + FLYING_GLYPH("enchantmenttable", Type.PARTICLE), + /** + * Fire particles + */ + FLAME("flame", Type.PARTICLE), + /** + * The particles that pop out of lava + */ + LAVA_POP("lava", Type.PARTICLE), + /** + * A small gray square + */ + FOOTSTEP("footstep", Type.PARTICLE), + /** + * Water particles + */ + SPLASH("splash", Type.PARTICLE), + /** + * Smoke particles + */ + PARTICLE_SMOKE("smoke", Type.PARTICLE), + /** + * The biggest explosion particle effect + */ + EXPLOSION_HUGE("hugeexplosion", Type.PARTICLE), + /** + * A larger version of the explode particle + */ + EXPLOSION_LARGE("largeexplode", Type.PARTICLE), + /** + * Explosion particles + */ + EXPLOSION("explode", Type.PARTICLE), + /** + * Small gray particles + */ + VOID_FOG("depthsuspend", Type.PARTICLE), + /** + * Small gray particles + */ + SMALL_SMOKE("townaura", Type.PARTICLE), + /** + * A puff of white smoke + */ + CLOUD("cloud", Type.PARTICLE), + /** + * Multicolored dust particles + */ + COLOURED_DUST("reddust", Type.PARTICLE), + /** + * Snowball breaking + */ + SNOWBALL_BREAK("snowballpoof", Type.PARTICLE), + /** + * The water drip particle that appears on blocks under water + */ + WATERDRIP("dripWater", Type.PARTICLE), + /** + * The lava drip particle that appears on blocks under lava + */ + LAVADRIP("dripLava", Type.PARTICLE), + /** + * White particles + */ + SNOW_SHOVEL("snowshovel", Type.PARTICLE), + /** + * The particle shown when a slime jumps + */ + SLIME("slime", Type.PARTICLE), + /** + * The particle that appears when breading animals + */ + HEART("heart", Type.PARTICLE), + /** + * The particle that appears when hitting a villager + */ + VILLAGER_THUNDERCLOUD("angryVillager", Type.PARTICLE), + /** + * The particle that appears when trading with a villager + */ + HAPPY_VILLAGER("happyVillager", Type.PARTICLE), + /** + * The particles generated when a tool breaks. + * This particle requires a Material so that the client can select the correct texture. + */ + ITEM_BREAK("iconcrack", Type.PARTICLE, Material.class), + /** + * The particles generated while breaking a block. + * This particle requires a Material and data value so that the client can select the correct texture. + */ + TILE_BREAK("tilecrack", Type.PARTICLE, MaterialData.class); private final int id; private final Type type; private final Class data; private static final Map BY_ID = Maps.newHashMap(); + private static final Map BY_NAME = Maps.newHashMap(); + private final String particleName; - Effect(int id, Type type) { + private Effect(int id, Type type) { this(id,type,null); } - Effect(int id, Type type, Class data) { + private Effect(int id, Type type, Class data) { this.id = id; this.type = type; this.data = data; + particleName = null; + } + + private Effect(String particleName, Type type, Class data) { + this.particleName = particleName; + this.type = type; + id = 0; + this.data = data; + } + + private Effect(String particleName, Type type) { + this.particleName = particleName; + this.type = type; + id = 0; + this.data = null; } /** * Gets the ID for this effect. * - * @return ID of this effect + * @return if this Effect isn't of type PARTICLE it returns ID of this effect * @deprecated Magic value */ @Deprecated @@ -106,6 +258,15 @@ public int getId() { return this.id; } + /** + * Returns the effect's name. This returns null if the effect is not a particle + * + * @return The effect's name + */ + public String getName() { + return particleName; + } + /** * @return The type of the effect. */ @@ -114,7 +275,7 @@ public Type getType() { } /** - * @return The class which represents data for this effect, or null if none + * @return if this Effect isn't of type PARTICLE it returns the class which represents data for this effect, or null if none */ public Class getData() { return this.data; @@ -134,12 +295,32 @@ public static Effect getById(int id) { static { for (Effect effect : values()) { - BY_ID.put(effect.id, effect); + if (effect.type != Type.PARTICLE) { + BY_ID.put(effect.id, effect); + } + } + } + + /** + * Gets the Effect associated with the given name. + * + * @param name name of the Effect to return + * @return Effect with the given name + */ + public static Effect getByName(String name) { + return BY_NAME.get(name); + } + + static { + for (Effect effect : values()) { + if (effect.type == Type.PARTICLE) { + BY_NAME.put(effect.particleName, effect); + } } } /** * Represents the type of an effect. */ - public enum Type {SOUND, VISUAL} + public enum Type {SOUND, VISUAL, PARTICLE} } diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java index 62fadda7..39c72c5c 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -1070,6 +1070,56 @@ public interface World extends PluginMessageRecipient, Metadatable { */ public boolean isGameRule(String rule); + // Spigot start + public class Spigot + { + + /** + * Plays an effect to all players within a default radius around a given + * location. + * + * @param location the {@link Location} around which players must be to + * see the effect + * @param effect the {@link Effect} + * @throws IllegalArgumentException if the location or effect is null. + * It also throws when the effect requires a material or a material data + */ + public void playEffect(Location location, Effect effect) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Plays an effect to all players within a default radius around a given + * location. The effect will use the provided material (and material + * data if required). The particle's position on the client will be the + * given location, adjusted on each axis by a normal distribution with + * mean 0 and standard deviation given in the offset parameters, each + * particle has independently calculated offsets. The effect will have + * the given speed and particle count if the effect is a particle. Some + * effect will create multiple particles. + * + * @param location the {@link Location} around which players must be to + * see the effect + * @param effect effect the {@link Effect} + * @param id the item/block/data id for the effect + * @param data the data value of the block/item for the effect + * @param offsetX the amount to be randomly offset by in the X axis + * @param offsetY the amount to be randomly offset by in the Y axis + * @param offsetZ the amount to be randomly offset by in the Z axis + * @param speed the speed of the particles + * @param particleCount the number of particles + * @param radius the radius around the location + */ + public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot end + /** * Represents various map environment types that a world may be */ diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java index fb3c90fb..ffbcac1f 100644 --- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java @@ -20,6 +20,7 @@ public ReloadCommand(String name) { public boolean execute(CommandSender sender, String currentAlias, String[] args) { if (!testPermission(sender)) return true; + org.spigotmc.CustomTimingsHandler.reload(); // Spigot: TODO: Why is this here? Bukkit.reload(); Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); diff --git a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java b/src/main/java/org/bukkit/command/defaults/TimingsCommand.java index 05cfcb01..ec1320ff 100644 --- a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java +++ b/src/main/java/org/bukkit/command/defaults/TimingsCommand.java @@ -19,30 +19,60 @@ import com.google.common.collect.ImmutableList; +// Spigot start +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.logging.Level; +// Spigot end + public class TimingsCommand extends BukkitCommand { private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("merged", "reset", "separate"); + public static long timingStart = 0; // Spigot public TimingsCommand(String name) { super(name); this.description = "Records timings for all plugin events"; - this.usageMessage = "/timings "; + this.usageMessage = "/timings [paste]"; // Spigot this.setPermission("bukkit.command.timings"); } @Override public boolean execute(CommandSender sender, String currentAlias, String[] args) { if (!testPermission(sender)) return true; - if (args.length != 1) { + if (args.length < 1) { // Spigot sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); return false; } - if (!sender.getServer().getPluginManager().useTimings()) { + /*if (!sender.getServer().getPluginManager().useTimings()) { sender.sendMessage("Please enable timings by setting \"settings.plugin-profiling\" to true in bukkit.yml"); return true; + }*/ + + // Spigot start - dynamic enable + if ( "on".equals( args[0] ) ) + { + ( (org.bukkit.plugin.SimplePluginManager) Bukkit.getPluginManager() ).useTimings( true ); + sender.sendMessage( "Enabled Timings" ); + } else if ( "off".equals( args[0] ) ) + { + ( (org.bukkit.plugin.SimplePluginManager) Bukkit.getPluginManager() ).useTimings( false ); + sender.sendMessage( "Disabled Timings" ); } + // Spigot end boolean separate = "separate".equals(args[0]); - if ("reset".equals(args[0])) { + boolean paste = "paste".equals( args[0] ); // Spigot + if ("on".equals(args[0]) || "reset".equals(args[0])) { // Spigot + // Spigot start + if ( !"on".equals( args[0] ) && !Bukkit.getPluginManager().useTimings() ) + { + sender.sendMessage( "Please enable timings by typing /timings on" ); + return true; + } + // Spigot end for (HandlerList handlerList : HandlerList.getHandlerLists()) { for (RegisteredListener listener : handlerList.getRegisteredListeners()) { if (listener instanceof TimedRegisteredListener) { @@ -50,20 +80,30 @@ public boolean execute(CommandSender sender, String currentAlias, String[] args) } } } + // Spigot start + org.spigotmc.CustomTimingsHandler.reload(); + timingStart = System.nanoTime(); sender.sendMessage("Timings reset"); - } else if ("merged".equals(args[0]) || separate) { - + } else if ("merged".equals(args[0]) || separate || paste) { + if ( !Bukkit.getPluginManager().useTimings() ) + { + sender.sendMessage( "Please enable timings by typing /timings on" ); + return true; + } + long sampleTime = System.nanoTime() - timingStart; + // Spigot end int index = 0; int pluginIdx = 0; File timingFolder = new File("timings"); timingFolder.mkdirs(); File timings = new File(timingFolder, "timings.txt"); File names = null; + ByteArrayOutputStream bout = ( paste ) ? new ByteArrayOutputStream() : null; // Spigot while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt"); PrintStream fileTimings = null; PrintStream fileNames = null; try { - fileTimings = new PrintStream(timings); + fileTimings = ( paste ) ? new PrintStream( bout ) : new PrintStream( timings ); if (separate) { names = new File(timingFolder, "names" + index + ".txt"); fileNames = new PrintStream(names); @@ -86,13 +126,25 @@ public boolean execute(CommandSender sender, String currentAlias, String[] args) totalTime += time; Class eventClass = trl.getEventClass(); if (count > 0 && eventClass != null) { - fileTimings.println(" " + eventClass.getSimpleName() + (trl.hasMultiple() ? " (and sub-classes)" : "") + " Time: " + time + " Count: " + count + " Avg: " + avg); + fileTimings.println(" " + eventClass.getSimpleName() + (trl.hasMultiple() ? " (and sub-classes)" : "") + " Time: " + time + " Count: " + count + " Avg: " + avg + " Violations: " + trl.violations); // Spigot } } } fileTimings.println(" Total time " + totalTime + " (" + totalTime / 1000000000 + "s)"); } + + // Spigot start + org.spigotmc.CustomTimingsHandler.printTimings(fileTimings); + fileTimings.println( "Sample time " + sampleTime + " (" + sampleTime / 1E9 + "s)" ); // Spigot + // Spigot start + if ( paste ) + { + new PasteThread( sender, bout ).start(); + return true; + } + // Spigot end sender.sendMessage("Timings written to " + timings.getPath()); + sender.sendMessage( "Paste contents of file into form at http://aikar.co/timings.php to read results." ); if (separate) sender.sendMessage("Names written to " + names.getPath()); } catch (IOException e) { } finally { @@ -118,4 +170,47 @@ public List tabComplete(CommandSender sender, String alias, String[] arg } return ImmutableList.of(); } + + // Spigot start + private static class PasteThread extends Thread + { + + private final CommandSender sender; + private final ByteArrayOutputStream bout; + + public PasteThread(CommandSender sender, ByteArrayOutputStream bout) + { + super( "Timings paste thread" ); + this.sender = sender; + this.bout = bout; + } + + @Override + public void run() + { + try + { + HttpURLConnection con = (HttpURLConnection) new URL( "http://paste.ubuntu.com/" ).openConnection(); + con.setDoOutput( true ); + con.setRequestMethod( "POST" ); + con.setInstanceFollowRedirects( false ); + + OutputStream out = con.getOutputStream(); + out.write( "poster=Spigot&syntax=text&content=".getBytes( "UTF-8" ) ); + out.write( URLEncoder.encode( bout.toString( "UTF-8" ), "UTF-8" ).getBytes( "UTF-8" ) ); + out.close(); + con.getInputStream().close(); + + String location = con.getHeaderField( "Location" ); + String pasteID = location.substring( "http://paste.ubuntu.com/".length(), location.length() - 1 ); + sender.sendMessage( ChatColor.GREEN + "Your timings have been pasted to " + location ); + sender.sendMessage( ChatColor.GREEN + "You can view the results at http://aikar.co/timings.php?url=" + pasteID ); + } catch ( IOException ex ) + { + sender.sendMessage( ChatColor.RED + "Error pasting timings, check your console for more information" ); + Bukkit.getServer().getLogger().log( Level.WARNING, "Could not paste timings", ex ); + } + } + } + // Spigot end } diff --git a/src/main/java/org/bukkit/conversations/BooleanPrompt.java b/src/main/java/org/bukkit/conversations/BooleanPrompt.java index 6abb3547..a51e7543 100644 --- a/src/main/java/org/bukkit/conversations/BooleanPrompt.java +++ b/src/main/java/org/bukkit/conversations/BooleanPrompt.java @@ -14,12 +14,13 @@ public BooleanPrompt() { @Override protected boolean isInputValid(ConversationContext context, String input) { - String[] accepted = {"true", "false", "on", "off", "yes", "no"}; + String[] accepted = {"true", "false", "on", "off", "yes", "no" /* Spigot: */, "y", "n", "1", "0", "right", "wrong", "correct", "incorrect", "valid", "invalid"}; // Spigot return ArrayUtils.contains(accepted, input.toLowerCase()); } @Override protected Prompt acceptValidatedInput(ConversationContext context, String input) { + if (input.equalsIgnoreCase("y") || input.equals("1") || input.equalsIgnoreCase("right") || input.equalsIgnoreCase("correct") || input.equalsIgnoreCase("valid")) input = "true"; // Spigot return acceptValidatedInput(context, BooleanUtils.toBoolean(input)); } diff --git a/src/main/java/org/bukkit/conversations/Conversation.java b/src/main/java/org/bukkit/conversations/Conversation.java index a30745fe..55c97858 100644 --- a/src/main/java/org/bukkit/conversations/Conversation.java +++ b/src/main/java/org/bukkit/conversations/Conversation.java @@ -193,6 +193,7 @@ public ConversationState getState() { * @param input The user's chat text. */ public void acceptInput(String input) { + try { // Spigot if (currentPrompt != null) { // Echo the user's input @@ -212,6 +213,12 @@ public void acceptInput(String input) { currentPrompt = currentPrompt.acceptInput(context, input); outputNextPrompt(); } + // Spigot Start + } catch ( Throwable t ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.SEVERE, "Error handling conversation prompt", t ); + } + // Spigot End } /** diff --git a/src/main/java/org/bukkit/conversations/ConversationContext.java b/src/main/java/org/bukkit/conversations/ConversationContext.java index 7a5b5edc..6b4bcace 100644 --- a/src/main/java/org/bukkit/conversations/ConversationContext.java +++ b/src/main/java/org/bukkit/conversations/ConversationContext.java @@ -43,6 +43,14 @@ public Conversable getForWhom() { return forWhom; } + /** + * Gets the entire sessionData map. + * @return The full sessionData map. + */ + public Map getAllSessionData() { + return sessionData; + } + /** * Gets session data shared between all {@link Prompt} invocations. Use this as a way * to pass data through each Prompt as the conversation develops. diff --git a/src/main/java/org/bukkit/entity/Arrow.java b/src/main/java/org/bukkit/entity/Arrow.java index 26d34739..676fe2bf 100644 --- a/src/main/java/org/bukkit/entity/Arrow.java +++ b/src/main/java/org/bukkit/entity/Arrow.java @@ -3,4 +3,22 @@ /** * Represents an arrow. */ -public interface Arrow extends Projectile {} +public interface Arrow extends Projectile +{ + + public class Spigot extends Entity.Spigot + { + + public double getDamage() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void setDamage(double damage) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); +} diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java index 72af4fa1..a5b21003 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java @@ -280,4 +280,22 @@ public interface Entity extends Metadatable { * @return The current vehicle. */ public Entity getVehicle(); + + // Spigot Start + public class Spigot + { + + /** + * Returns whether this entity is invulnerable. + * + * @return True if the entity is invulnerable. + */ + public boolean isInvulnerable() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot End } diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java index 3ec374b4..5ebb9e78 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -721,4 +721,79 @@ public interface Player extends HumanEntity, Conversable, CommandSender, Offline * @see Player#setHealthScaled(boolean) */ public double getHealthScale(); + + // Spigot start + public class Spigot extends Entity.Spigot + { + + /** + * Gets the connection address of this player, regardless of whether it + * has been spoofed or not. + * + * @return the player's connection address + */ + public InetSocketAddress getRawAddress() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Gets whether the player collides with entities + * + * @return the player's collision toggle state + */ + public boolean getCollidesWithEntities() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sets whether the player collides with entities + * + * @param collides whether the player should collide with entities or + * not. + */ + public void setCollidesWithEntities(boolean collides) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Respawns the player if dead. + */ + public void respawn() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Gets the player's experience cooldown + * + * @return the player's experience cooldown + */ + public int getExperienceCooldown() + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + + /** + * Sets the player's experience cooldown + * + * @param cooldown how much time is needed for the player + * to gain experience points. A cooldown of 0 disables + * experience point pickup. + */ + public void setExperienceCooldown(int cooldown) + { + throw new UnsupportedOperationException( "Not supported yet." ); + } + } + + Spigot spigot(); + // Spigot end } diff --git a/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java b/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java index f60c86ff..303479dd 100644 --- a/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java +++ b/src/main/java/org/bukkit/event/entity/CreatureSpawnEvent.java @@ -4,17 +4,13 @@ import org.bukkit.entity.CreatureType; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; /** * Called when a creature is spawned into a world. *

* If a Creature Spawn event is cancelled, the creature will not spawn. */ -public class CreatureSpawnEvent extends EntityEvent implements Cancellable { - private static final HandlerList handlers = new HandlerList(); - private boolean canceled; +public class CreatureSpawnEvent extends EntitySpawnEvent { private final SpawnReason spawnReason; public CreatureSpawnEvent(final LivingEntity spawnee, final SpawnReason spawnReason) { @@ -28,28 +24,11 @@ public CreatureSpawnEvent(Entity spawnee, CreatureType type, Location loc, Spawn spawnReason = reason; } - public boolean isCancelled() { - return canceled; - } - - public void setCancelled(boolean cancel) { - canceled = cancel; - } - @Override public LivingEntity getEntity() { return (LivingEntity) entity; } - /** - * Gets the location at which the creature is spawning. - * - * @return The location at which the creature is spawning - */ - public Location getLocation() { - return getEntity().getLocation(); - } - /** * Gets the type of creature being spawned. * @@ -70,15 +49,6 @@ public SpawnReason getSpawnReason() { return spawnReason; } - @Override - public HandlerList getHandlers() { - return handlers; - } - - public static HandlerList getHandlerList() { - return handlers; - } - /** * An enum to specify the type of spawning */ diff --git a/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java b/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java new file mode 100644 index 00000000..5dcf98f3 --- /dev/null +++ b/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java @@ -0,0 +1,45 @@ +package org.bukkit.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; + +/** + * Called when an entity is spawned into a world. + *

+ * If an Entity Spawn event is cancelled, the entity will not spawn. + */ +public class EntitySpawnEvent extends EntityEvent implements org.bukkit.event.Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + + public EntitySpawnEvent(final Entity spawnee) { + super(spawnee); + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the location at which the entity is spawning. + * + * @return The location at which the entity is spawning + */ + public Location getLocation() { + return getEntity().getLocation(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java b/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java index bafd934a..776f8e72 100644 --- a/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java +++ b/src/main/java/org/bukkit/event/entity/ItemSpawnEvent.java @@ -1,51 +1,23 @@ package org.bukkit.event.entity; -import org.bukkit.entity.Item; import org.bukkit.Location; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; +import org.bukkit.entity.Item; /** * Called when an item is spawned into a world */ -public class ItemSpawnEvent extends EntityEvent implements Cancellable { - private static final HandlerList handlers = new HandlerList(); - private final Location location; - private boolean canceled; - - public ItemSpawnEvent(final Item spawnee, final Location loc) { +public class ItemSpawnEvent extends EntitySpawnEvent { + public ItemSpawnEvent(final Item spawnee) { super(spawnee); - this.location = loc; } - public boolean isCancelled() { - return canceled; - } - - public void setCancelled(boolean cancel) { - canceled = cancel; + @Deprecated + public ItemSpawnEvent(final Item spawnee, final Location loc) { + this(spawnee); } @Override public Item getEntity() { return (Item) entity; } - - /** - * Gets the location at which the item is spawning. - * - * @return The location at which the item is spawning - */ - public Location getLocation() { - return location; - } - - @Override - public HandlerList getHandlers() { - return handlers; - } - - public static HandlerList getHandlerList() { - return handlers; - } } diff --git a/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java b/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java new file mode 100644 index 00000000..1acb3c40 --- /dev/null +++ b/src/main/java/org/bukkit/event/entity/SpawnerSpawnEvent.java @@ -0,0 +1,22 @@ +package org.bukkit.event.entity; + +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.Entity; + +/** + * Called when an entity is spawned into a world by a spawner. + *

+ * If a Spawner Spawn event is cancelled, the entity will not spawn. + */ +public class SpawnerSpawnEvent extends EntitySpawnEvent { + private final CreatureSpawner spawner; + + public SpawnerSpawnEvent(final Entity spawnee, final CreatureSpawner spawner) { + super(spawnee); + this.spawner = spawner; + } + + public CreatureSpawner getSpawner() { + return spawner; + } +} diff --git a/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java b/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java index 28198b8b..3313d915 100644 --- a/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java +++ b/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java @@ -47,6 +47,7 @@ public class InventoryClickEvent extends InventoryInteractEvent { private static final HandlerList handlers = new HandlerList(); private final ClickType click; private final InventoryAction action; + private final Inventory clickedInventory; private SlotType slot_type; private int whichSlot; private int rawSlot; @@ -62,6 +63,13 @@ public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickTyp super(view); this.slot_type = type; this.rawSlot = slot; + if (slot < 0) { + this.clickedInventory = null; + } else if (view.getTopInventory() != null && slot < view.getTopInventory().getSize()) { + this.clickedInventory = view.getTopInventory(); + } else { + this.clickedInventory = view.getBottomInventory(); + } this.whichSlot = view.convertSlot(slot); this.click = click; this.action = action; @@ -72,6 +80,14 @@ public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickTyp this.hotbarKey = key; } + /** + * Gets the inventory that was clicked, or null if outside of window + * @return The clicked inventory + */ + public Inventory getClickedInventory() { + return clickedInventory; + } + /** * Gets the type of slot that was clicked. * diff --git a/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java b/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java new file mode 100644 index 00000000..38a72ab8 --- /dev/null +++ b/src/main/java/org/bukkit/event/player/PlayerItemDamageEvent.java @@ -0,0 +1,54 @@ +package org.bukkit.event.player; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +public class PlayerItemDamageEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private final ItemStack item; + private int damage; + private boolean cancelled = false; + + public PlayerItemDamageEvent(Player player, ItemStack what, int damage) { + super(player); + this.item = what; + this.damage = damage; + } + + public ItemStack getItem() { + return item; + } + + /** + * Gets the amount of durability damage this item will be taking. + * + * @return durability change + */ + public int getDamage() { + return damage; + } + + public void setDamage(int damage) { + this.damage = damage; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java index 60c08756..dab3c5c0 100644 --- a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java +++ b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java @@ -14,6 +14,7 @@ public class PlayerLoginEvent extends PlayerEvent { private final String hostname; private Result result = Result.ALLOWED; private String message = ""; + private final InetAddress realAddress; // Spigot /** * @deprecated Address should be provided in other constructor @@ -38,10 +39,17 @@ public PlayerLoginEvent(final Player player, final String hostname) { * @param hostname The hostname that was used to connect to the server * @param address The address the player used to connect, provided for timing issues */ - public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address) { + public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address, final InetAddress realAddress) { // Spigot super(player); this.hostname = hostname; this.address = address; + // Spigot start + this.realAddress = address; + } + + public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address) { + this(player, hostname, address, address); + // Spigot end } /** @@ -49,7 +57,7 @@ public PlayerLoginEvent(final Player player, final String hostname, final InetAd */ @Deprecated public PlayerLoginEvent(final Player player, final Result result, final String message) { - this(player, "", null, result, message); + this(player, "", null, result, message, null); // Spigot } /** @@ -61,12 +69,23 @@ public PlayerLoginEvent(final Player player, final Result result, final String m * @param result The result status for this event * @param message The message to be displayed if result denies login */ - public PlayerLoginEvent(final Player player, String hostname, final InetAddress address, final Result result, final String message) { - this(player, hostname, address); + public PlayerLoginEvent(final Player player, String hostname, final InetAddress address, final Result result, final String message, final InetAddress realAddress) { // Spigot + this(player, hostname, address, realAddress); // Spigot this.result = result; this.message = message; } + // Spigot start + /** + * Gets the connection address of this player, regardless of whether it has been spoofed or not. + * + * @return the player's connection address + */ + public InetAddress getRealAddress() { + return realAddress; + } + // Spigot end + /** * Gets the current result of the login, as an enum * diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java index 93fb4f5a..a96a33b4 100644 --- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -274,6 +274,7 @@ public Plugin[] loadPlugins(File directory) { } } + org.bukkit.command.defaults.TimingsCommand.timingStart = System.nanoTime(); // Spigot return result.toArray(new Plugin[result.size()]); } diff --git a/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java b/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java index d86805b9..4b744eaa 100644 --- a/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java +++ b/src/main/java/org/bukkit/plugin/TimedRegisteredListener.java @@ -11,6 +11,10 @@ public class TimedRegisteredListener extends RegisteredListener { private int count; private long totalTime; + // Spigot start + public long curTickTotal = 0; + public long violations = 0; + // Spigot end private Class eventClass; private boolean multiple = false; @@ -20,6 +24,13 @@ public TimedRegisteredListener(final Listener pluginListener, final EventExecuto @Override public void callEvent(Event event) throws EventException { + // Spigot start + if ( org.bukkit.Bukkit.getServer() != null && !org.bukkit.Bukkit.getServer().getPluginManager().useTimings() ) + { + super.callEvent( event ); + return; + } + // Spigot end if (event.isAsynchronous()) { super.callEvent(event); return; @@ -34,7 +45,11 @@ public void callEvent(Event event) throws EventException { } long start = System.nanoTime(); super.callEvent(event); - totalTime += System.nanoTime() - start; + // Spigot start + long diff = System.nanoTime() - start; + curTickTotal += diff; + totalTime += diff; + // Spigot end } private static Class getCommonSuperclass(Class class1, Class class2) { @@ -50,6 +65,10 @@ private static Class getCommonSuperclass(Class class1, Class class2) { public void reset() { count = 0; totalTime = 0; + // Spigot start + curTickTotal = 0; + violations = 0; + // Spigot end } /** diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java index ea30d83d..d905435a 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -430,7 +430,7 @@ public void execute(Listener listener, Event event) throws EventException { } } }; - if (useTimings) { + if (true) { // Spigot - TRL handles useTimings check now eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); } else { eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java new file mode 100644 index 00000000..9fca481d --- /dev/null +++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java @@ -0,0 +1,175 @@ +package org.spigotmc; + +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; +import org.bukkit.plugin.TimedRegisteredListener; +import java.io.PrintStream; +import java.util.Collection; +import java.util.HashSet; +import org.bukkit.Bukkit; +import org.bukkit.World; + +/** + * Provides custom timing sections for /timings merged. + */ +public class CustomTimingsHandler +{ + + private static final Collection ALL_HANDLERS = new HashSet(); + private static CustomTimingsHandler[] BAKED_HANDLERS; + /*========================================================================*/ + private final String name; + private final CustomTimingsHandler parent; + private long count = 0; + private long start = 0; + private long timingDepth = 0; + private long totalTime = 0; + private long curTickTotal = 0; + private long violations = 0; + + public CustomTimingsHandler(String name) + { + this( name, null ); + } + + public CustomTimingsHandler(String name, CustomTimingsHandler parent) + { + this.name = name; + this.parent = parent; + ALL_HANDLERS.add( this ); + BAKED_HANDLERS = ALL_HANDLERS.toArray( new CustomTimingsHandler[ ALL_HANDLERS.size() ] ); + } + + /** + * Prints the timings and extra data to the given stream. + * + * @param printStream + */ + public static void printTimings(PrintStream printStream) + { + printStream.println( "Minecraft" ); + for ( CustomTimingsHandler timings : BAKED_HANDLERS ) + { + long time = timings.totalTime; + long count = timings.count; + if ( count == 0 ) + { + continue; + } + long avg = time / count; + + printStream.println( " " + timings.name + " Time: " + time + " Count: " + count + " Avg: " + avg + " Violations: " + timings.violations ); + } + printStream.println( "# Version " + Bukkit.getVersion() ); + int entities = 0; + int livingEntities = 0; + for ( World world : Bukkit.getWorlds() ) + { + entities += world.getEntities().size(); + livingEntities += world.getLivingEntities().size(); + } + printStream.println( "# Entities " + entities ); + printStream.println( "# LivingEntities " + livingEntities ); + } + + /** + * Resets all timings. + */ + public static void reload() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + for ( CustomTimingsHandler timings : BAKED_HANDLERS ) + { + timings.reset(); + } + } + } + + /** + * Ticked every tick by CraftBukkit to count the number of times a timer + * caused TPS loss. + */ + public static void tick() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + for ( CustomTimingsHandler timings : BAKED_HANDLERS ) + { + if ( timings.curTickTotal > 50000000 ) + { + timings.violations += Math.ceil( timings.curTickTotal / 50000000 ); + } + timings.curTickTotal = 0; + timings.timingDepth = 0; // incase reset messes this up + } + + for ( Plugin plugin : Bukkit.getPluginManager().getPlugins() ) + { + for ( RegisteredListener listener : HandlerList.getRegisteredListeners( plugin ) ) + { + if ( listener instanceof TimedRegisteredListener ) + { + TimedRegisteredListener timings = (TimedRegisteredListener) listener; + if ( timings.curTickTotal > 50000000 ) + { + timings.violations += Math.ceil( timings.curTickTotal / 50000000 ); + } + timings.curTickTotal = 0; + } + } + } + } + } + + /** + * Starts timing to track a section of code. + */ + public void startTiming() + { + // If second condtion fails we are already timing + if ( Bukkit.getPluginManager().useTimings() && ++timingDepth == 1 ) + { + start = System.nanoTime(); + if ( parent != null && ++parent.timingDepth == 1 ) + { + parent.start = start; + } + } + } + + /** + * Stops timing a section of code. + */ + public void stopTiming() + { + if ( Bukkit.getPluginManager().useTimings() ) + { + if ( --timingDepth != 0 || start == 0 ) + { + return; + } + long diff = System.nanoTime() - start; + totalTime += diff; + curTickTotal += diff; + count++; + start = 0; + if ( parent != null ) + { + parent.stopTiming(); + } + } + } + + /** + * Reset this timer, setting all values to zero. + */ + public void reset() + { + count = 0; + violations = 0; + curTickTotal = 0; + totalTime = 0; + } +} diff --git a/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java b/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java new file mode 100644 index 00000000..24d4942a --- /dev/null +++ b/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java @@ -0,0 +1,39 @@ +package org.spigotmc.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; + +/** + * Called when an entity stops riding another entity. + * + */ +public class EntityDismountEvent extends EntityEvent +{ + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity dismounted; + + public EntityDismountEvent(Entity what, Entity dismounted) + { + super( what ); + this.dismounted = dismounted; + } + + public Entity getDismounted() + { + return dismounted; + } + + @Override + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } +} diff --git a/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java b/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java new file mode 100644 index 00000000..16aa2a7e --- /dev/null +++ b/src/main/java/org/spigotmc/event/entity/EntityMountEvent.java @@ -0,0 +1,52 @@ +package org.spigotmc.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; + +/** + * Called when an entity attempts to ride another entity. + * + */ +public class EntityMountEvent extends EntityEvent implements Cancellable +{ + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity mount; + + public EntityMountEvent(Entity what, Entity mount) + { + super( what ); + this.mount = mount; + } + + public Entity getMount() + { + return mount; + } + + @Override + public boolean isCancelled() + { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) + { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } +} diff --git a/src/test/java/org/bukkit/EffectTest.java b/src/test/java/org/bukkit/EffectTest.java index 08aa71d3..5217aecb 100644 --- a/src/test/java/org/bukkit/EffectTest.java +++ b/src/test/java/org/bukkit/EffectTest.java @@ -9,7 +9,11 @@ public class EffectTest { @Test public void getById() { for (Effect effect : Effect.values()) { - assertThat(Effect.getById(effect.getId()), is(effect)); + if (effect.getType() != Effect.Type.PARTICLE) { + assertThat(Effect.getById(effect.getId()), is(effect)); + } else { + assertThat(Effect.getByName(effect.getName()), is(effect)); + } } } } diff --git a/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java b/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java index b206b1f3..01b62fb5 100644 --- a/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java +++ b/src/test/java/org/bukkit/plugin/TimedRegisteredListenerTest.java @@ -15,7 +15,6 @@ public class TimedRegisteredListenerTest { - @Test public void testEventClass() throws EventException { Listener listener = new Listener() {}; EventExecutor executor = new EventExecutor() {