The start of the spigot

This commit is contained in:
beaness 2022-06-22 19:29:55 +02:00
commit cdffe91cc2
1710 changed files with 224784 additions and 0 deletions

69
.gitignore vendored Normal file
View File

@ -0,0 +1,69 @@
<<<<<<< HEAD
# Eclipse
/.classpath
/.project
/.settings
# Netbeans
/nbproject
# We active maven!
/build.xml
# Maven
/target
/dependency-reduced-pom.xml
*/target
*/dependency-reduced-pom.xml
# Vim
.*.sw[a-p]
# Various other potential build files
/build
/bin
/dist
/manifest.mf
/MANIFEST.MF
/META-INF/MANIFEST.MF
git.properties
.ssh
key
key.pub
dependency-reduced-pom.xml
# Mac Filesystem Dust
.DS_Store
# IntelliJ IDEA
*.iml
*.ipr
*.iws
.idea/
#Libraries jar files
libraries/*.jar
=======
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
>>>>>>> 354c5c7d98801e512c3da0913cd1824a9cf4b2cf

69
TacoSpigot-API/.gitignore vendored Normal file
View File

@ -0,0 +1,69 @@
<<<<<<< HEAD
# Eclipse
/.classpath
/.project
/.settings
# Netbeans
/nbproject
# We active maven!
/build.xml
# Maven
/target
/dependency-reduced-pom.xml
*/target
*/dependency-reduced-pom.xml
# Vim
.*.sw[a-p]
# Various other potential build files
/build
/bin
/dist
/manifest.mf
/MANIFEST.MF
/META-INF/MANIFEST.MF
git.properties
.ssh
key
key.pub
dependency-reduced-pom.xml
# Mac Filesystem Dust
.DS_Store
# IntelliJ IDEA
*.iml
*.ipr
*.iws
.idea/
#Libraries jar files
libraries/*.jar
=======
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
>>>>>>> 354c5c7d98801e512c3da0913cd1824a9cf4b2cf

174
TacoSpigot-API/pom.xml Normal file
View File

@ -0,0 +1,174 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.techcable.tacospigot</groupId>
<artifactId>parent</artifactId>
<version>dev-SNAPSHOT</version>
</parent>
<groupId>net.techcable.tacospigot</groupId>
<artifactId>api</artifactId>
<version>1.8.8-R0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>TacoSpigot-API</name>
<url>https://github.com/TacoSpigot</url>
<description>An enhanced plugin API for Minecraft servers.</description>
<properties>
<!--PaperSpigot - Bump to 1.8 - This will haunt me -->
<additionalparam>-Xdoclint:none</additionalparam>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- just until we get deployment to central approved -->
<distributionManagement>
<repository>
<id>destroystokyo-releases</id>
<url>https://repo.destroystokyo.com/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>destroystokyo-snapshots</id>
<url>https://repo.destroystokyo.com/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<!-- required until fixed plexus-compiler-eclipse is deployed -->
<pluginRepositories>
<pluginRepository>
<id>spigotmc-public</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
<version>3.0.3</version>
<!-- Trove Provided by CraftBukkit -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!-- bundled with Minecraft, should be kept in sync -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
<scope>compile</scope>
</dependency>
<!-- bundled with Minecraft, should be kept in sync -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>ebean</artifactId>
<version>2.8.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.15</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>1.8-SNAPSHOT</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- Thread Affinity -->
<dependency>
<groupId>net.openhft</groupId>
<artifactId>affinity</artifactId>
<version>3.20.0</version>
<scope>compile</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- versions after this appear to be broken -->
<version>3.1</version>
<configuration>
<!-- we use the Eclipse compiler as it doesn't need a JDK -->
<compilerId>eclipse</compilerId>
<!-- source and target are ignored if this isn't true -->
<optimize>true</optimize>
</configuration>
<dependencies>
<!-- we need our custom version as it fixes some bugs on case sensitive file systems -->
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-eclipse</artifactId>
<version>2.5.0-spigotmc</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- utterly useless artifact from shade 2.x -->
<createDependencyReducedPom>false</createDependencyReducedPom>
<!-- when downloading via Maven we can pull depends individually -->
<shadedArtifactAttached>true</shadedArtifactAttached>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,79 @@
package co.aikar.timings;
import static co.aikar.timings.TimingsManager.*;
public class FullServerTickHandler extends TimingHandler {
static final TimingIdentifier IDENTITY = new TimingIdentifier("Minecraft", "Full Server Tick", null, false);
final TimingData minuteData;
double avgFreeMemory = -1D;
double avgUsedMemory = -1D;
FullServerTickHandler() {
super(IDENTITY);
minuteData = new TimingData(id);
TIMING_MAP.put(IDENTITY, this);
}
@Override
public void startTiming() {
if (TimingsManager.needsFullReset) {
TimingsManager.resetTimings();
} else if (TimingsManager.needsRecheckEnabled) {
TimingsManager.recheckEnabled();
}
super.startTiming();
}
@Override
public void stopTiming() {
super.stopTiming();
if (!enabled) {
return;
}
if (TimingHistory.timedTicks % 20 == 0) {
final Runtime runtime = Runtime.getRuntime();
double usedMemory = runtime.totalMemory() - runtime.freeMemory();
double freeMemory = runtime.maxMemory() - usedMemory;
if (this.avgFreeMemory == -1) {
this.avgFreeMemory = freeMemory;
} else {
this.avgFreeMemory = (this.avgFreeMemory * (59 / 60D)) + (freeMemory * (1 / 60D));
}
if (this.avgUsedMemory == -1) {
this.avgUsedMemory = usedMemory;
} else {
this.avgUsedMemory = (this.avgUsedMemory * (59 / 60D)) + (usedMemory * (1 / 60D));
}
}
long start = System.nanoTime();
TimingsManager.tick();
long diff = System.nanoTime() - start;
CURRENT = TIMINGS_TICK;
TIMINGS_TICK.addDiff(diff);
// addDiff for TIMINGS_TICK incremented this, bring it back down to 1 per tick.
record.curTickCount--;
minuteData.curTickTotal = record.curTickTotal;
minuteData.curTickCount = 1;
boolean violated = isViolated();
minuteData.processTick(violated);
TIMINGS_TICK.processTick(violated);
processTick(violated);
if (TimingHistory.timedTicks % 1200 == 0) {
MINUTE_REPORTS.add(new TimingHistory.MinuteReport());
TimingHistory.resetTicks(false);
minuteData.reset();
}
if (TimingHistory.timedTicks % Timings.getHistoryInterval() == 0) {
TimingsManager.HISTORY.add(new TimingHistory());
TimingsManager.resetTimings();
}
}
boolean isViolated() {
return record.curTickTotal > 50000000;
}
}

View File

@ -0,0 +1,61 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
public final class NullTimingHandler implements Timing {
@Override
public void startTiming() {
}
@Override
public void stopTiming() {
}
@Override
public void startTimingIfSync() {
}
@Override
public void stopTimingIfSync() {
}
@Override
public void abort() {
}
@Override
public TimingHandler getTimingHandler() {
return null;
}
@Override
public void close() {
}
}

View File

@ -0,0 +1,81 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Method;
public class TimedEventExecutor implements EventExecutor {
private final EventExecutor executor;
private final Timing timings;
/**
* Wraps an event executor and associates a timing handler to it.
*
* @param executor
* @param plugin
* @param method
* @param eventClass
*/
public TimedEventExecutor(EventExecutor executor, Plugin plugin, Method method, Class<? extends Event> eventClass) {
this.executor = executor;
String id;
if (method == null) {
if (executor.getClass().getEnclosingClass() != null) { // Oh Skript, how we love you
method = executor.getClass().getEnclosingMethod();
}
}
if (method != null) {
id = method.getDeclaringClass().getName();
} else {
id = executor.getClass().getName();
}
final String eventName = eventClass.getSimpleName();
boolean verbose = "BlockPhysicsEvent".equals(eventName) || "Drain".equals(eventName) || "Fill".equals(eventName);
this.timings = Timings.ofSafe(plugin.getName(), (verbose ? "## " : "") +
"Event: " + id + " (" + eventName + ")", null);
}
@Override
public void execute(Listener listener, Event event) throws EventException {
if (event.isAsynchronous() || !Timings.timingsEnabled || !Bukkit.isPrimaryThread()) {
executor.execute(listener, event);
return;
}
timings.startTiming();
executor.execute(listener, event);
timings.stopTiming();
}
}

View File

@ -0,0 +1,72 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
/**
* Provides an ability to time sections of code within the Minecraft Server
*/
public interface Timing extends AutoCloseable {
/**
* Starts timing the execution until {@link #stopTiming()} is called.
*/
public void startTiming();
/**
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
*
* Will automatically be called when this Timing is used with try-with-resources
*/
public void stopTiming();
/**
* Starts timing the execution until {@link #stopTiming()} is called.
*
* But only if we are on the primary thread.
*/
public void startTimingIfSync();
/**
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
*
* <p>Will automatically be called when this Timing is used with try-with-resources</p>
*
* But only if we are on the primary thread.
*/
public void stopTimingIfSync();
/**
* Stops timing and disregards current timing data.
*/
public void abort();
/**
* Used internally to get the actual backing Handler in the case of delegated Handlers
*
* @return TimingHandler
*/
TimingHandler getTimingHandler();
@Override
void close();
}

View File

@ -0,0 +1,105 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import java.util.List;
import static co.aikar.util.JSONUtil.toArray;
/**
* <p>Lightweight object for tracking timing data</p>
*
* This is broken out to reduce memory usage
*/
class TimingData {
static Function<Integer, TimingData> LOADER = new Function<Integer, TimingData>() {
@Override
public TimingData apply(Integer input) {
return new TimingData(input);
}
};
int id;
int count = 0;
int lagCount = 0;
long totalTime = 0;
long lagTotalTime = 0;
int curTickCount = 0;
int curTickTotal = 0;
TimingData(int id) {
this.id = id;
}
TimingData(TimingData data) {
this.id = data.id;
this.totalTime = data.totalTime;
this.lagTotalTime = data.lagTotalTime;
this.count = data.count;
this.lagCount = data.lagCount;
}
void add(long diff) {
++curTickCount;
curTickTotal += diff;
}
void processTick(boolean violated) {
totalTime += curTickTotal;
count += curTickCount;
if (violated) {
lagTotalTime += curTickTotal;
lagCount += curTickCount;
}
curTickTotal = 0;
curTickCount = 0;
}
void reset() {
count = 0;
lagCount = 0;
curTickTotal = 0;
curTickCount = 0;
totalTime = 0;
lagTotalTime = 0;
}
protected TimingData clone() {
return new TimingData(this);
}
public List export() {
List list = toArray(
id,
count,
totalTime);
if (lagCount > 0) {
list.add(lagCount);
list.add(lagTotalTime);
}
return list;
}
}

View File

@ -0,0 +1,193 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import gnu.trove.map.hash.TIntObjectHashMap;
import org.bukkit.Bukkit;
import co.aikar.util.LoadingIntMap;
import co.aikar.util.LoadingMap;
import co.aikar.util.MRUMapCache;
import java.util.Map;
import java.util.logging.Level;
class TimingHandler implements Timing {
private static int idPool = 1;
final int id = idPool++;
final String name;
final boolean verbose;
final TIntObjectHashMap<TimingData> children = new LoadingIntMap<TimingData>(TimingData.LOADER);
final TimingData record;
final TimingHandler groupHandler;
long start = 0;
int timingDepth = 0;
boolean added;
boolean timed;
boolean enabled;
TimingHandler parent;
TimingHandler(TimingIdentifier id) {
if (id.name.startsWith("##")) {
verbose = true;
this.name = id.name.substring(3);
} else {
this.name = id.name;
verbose = false;
}
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
TimingIdentifier.getGroup(id.group).handlers.add(this);
checkEnabled();
}
final void checkEnabled() {
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
}
void processTick(boolean violated) {
if (timingDepth != 0 || record.curTickCount == 0) {
timingDepth = 0;
start = 0;
return;
}
record.processTick(violated);
for (TimingData handler : children.valueCollection()) {
handler.processTick(violated);
}
}
@Override
public void startTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
startTiming();
}
}
@Override
public void stopTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
stopTiming();
}
}
public void startTiming() {
if (enabled && ++timingDepth == 1) {
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
}
}
public void stopTiming() {
if (enabled && --timingDepth == 0 && start != 0) {
if (!Bukkit.isPrimaryThread()) {
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
new Throwable().printStackTrace();
start = 0;
return;
}
addDiff(System.nanoTime() - start);
start = 0;
}
}
@Override
public void abort() {
if (enabled && timingDepth > 0) {
start = 0;
}
}
void addDiff(long diff) {
if (TimingsManager.CURRENT == this) {
TimingsManager.CURRENT = parent;
if (parent != null) {
parent.children.get(id).add(diff);
}
}
record.add(diff);
if (!added) {
added = true;
timed = true;
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff);
groupHandler.children.get(id).add(diff);
}
}
/**
* Reset this timer, setting all values to zero.
*
* @param full
*/
void reset(boolean full) {
record.reset();
if (full) {
timed = false;
}
start = 0;
timingDepth = 0;
added = false;
children.clear();
checkEnabled();
}
@Override
public TimingHandler getTimingHandler() {
return this;
}
@Override
public boolean equals(Object o) {
return (this == o);
}
@Override
public int hashCode() {
return id;
}
/**
* This is simply for the Closeable interface so it can be used with
* try-with-resources ()
*/
@Override
public void close() {
stopTimingIfSync();
}
public boolean isSpecial() {
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
}
}

View File

@ -0,0 +1,276 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import com.google.common.collect.Sets;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import co.aikar.util.LoadingMap;
import co.aikar.util.MRUMapCache;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static co.aikar.timings.TimingsManager.FULL_SERVER_TICK;
import static co.aikar.timings.TimingsManager.MINUTE_REPORTS;
import static co.aikar.util.JSONUtil.*;
@SuppressWarnings({"deprecation", "SuppressionAnnotation"})
public class TimingHistory {
public static long lastMinuteTime;
public static long timedTicks;
public static long playerTicks;
public static long entityTicks;
public static long tileEntityTicks;
public static long activatedEntityTicks;
static int worldIdPool = 1;
static Map<String, Integer> worldMap = LoadingMap.newHashMap(new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return worldIdPool++;
}
});
final long endTime;
final long startTime;
final long totalTicks;
final long totalTime; // Represents all time spent running the server this history
final MinuteReport[] minuteReports;
final TimingHistoryEntry[] entries;
final Set<Material> tileEntityTypeSet = Sets.newHashSet();
final Set<EntityType> entityTypeSet = Sets.newHashSet();
final Map<Object, Object> worlds;
TimingHistory() {
this.endTime = System.currentTimeMillis() / 1000;
this.startTime = TimingsManager.historyStart / 1000;
if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) {
this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]);
this.minuteReports[this.minuteReports.length - 1] = new MinuteReport();
} else {
this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]);
}
long ticks = 0;
for (MinuteReport mp : this.minuteReports) {
ticks += mp.ticksRecord.timed;
}
this.totalTicks = ticks;
this.totalTime = FULL_SERVER_TICK.record.totalTime;
this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()];
int i = 0;
for (TimingHandler handler : TimingsManager.HANDLERS) {
entries[i++] = new TimingHistoryEntry(handler);
}
final Map<EntityType, Counter> entityCounts = MRUMapCache.of(LoadingMap.of(
new EnumMap<EntityType, Counter>(EntityType.class), Counter.LOADER
));
final Map<Material, Counter> tileEntityCounts = MRUMapCache.of(LoadingMap.of(
new EnumMap<Material, Counter>(Material.class), Counter.LOADER
));
// Information about all loaded chunks/entities
this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function<World, JSONPair>() {
@Override
public JSONPair apply(World world) {
return pair(
worldMap.get(world.getName()),
toArrayMapper(world.getLoadedChunks(), new Function<Chunk, Object>() {
@Override
public Object apply(Chunk chunk) {
entityCounts.clear();
tileEntityCounts.clear();
for (Entity entity : chunk.getEntities()) {
entityCounts.get(entity.getType()).increment();
}
for (BlockState tileEntity : chunk.getTileEntities()) {
tileEntityCounts.get(tileEntity.getBlock().getType()).increment();
}
if (tileEntityCounts.isEmpty() && entityCounts.isEmpty()) {
return null;
}
return toArray(
chunk.getX(),
chunk.getZ(),
toObjectMapper(entityCounts.entrySet(),
new Function<Map.Entry<EntityType, Counter>, JSONPair>() {
@Override
public JSONPair apply(Map.Entry<EntityType, Counter> entry) {
entityTypeSet.add(entry.getKey());
return pair(
String.valueOf(entry.getKey().getTypeId()),
entry.getValue().count()
);
}
}
),
toObjectMapper(tileEntityCounts.entrySet(),
new Function<Map.Entry<Material, Counter>, JSONPair>() {
@Override
public JSONPair apply(Map.Entry<Material, Counter> entry) {
tileEntityTypeSet.add(entry.getKey());
return pair(
String.valueOf(entry.getKey().getId()),
entry.getValue().count()
);
}
}
)
);
}
})
);
}
});
}
public static void resetTicks(boolean fullReset) {
if (fullReset) {
// Non full is simply for 1 minute reports
timedTicks = 0;
}
lastMinuteTime = System.nanoTime();
playerTicks = 0;
tileEntityTicks = 0;
entityTicks = 0;
activatedEntityTicks = 0;
}
Object export() {
return createObject(
pair("s", startTime),
pair("e", endTime),
pair("tk", totalTicks),
pair("tm", totalTime),
pair("w", worlds),
pair("h", toArrayMapper(entries, new Function<TimingHistoryEntry, Object>() {
@Override
public Object apply(TimingHistoryEntry entry) {
TimingData record = entry.data;
if (record.count == 0) {
return null;
}
return entry.export();
}
})),
pair("mp", toArrayMapper(minuteReports, new Function<MinuteReport, Object>() {
@Override
public Object apply(MinuteReport input) {
return input.export();
}
}))
);
}
static class MinuteReport {
final long time = System.currentTimeMillis() / 1000;
final TicksRecord ticksRecord = new TicksRecord();
final PingRecord pingRecord = new PingRecord();
final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone();
final double tps = 1E9 / ( System.nanoTime() - lastMinuteTime ) * ticksRecord.timed;
final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory;
final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory;
final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
public List export() {
return toArray(
time,
Math.round(tps * 100D) / 100D,
Math.round(pingRecord.avg * 100D) / 100D,
fst.export(),
toArray(ticksRecord.timed,
ticksRecord.player,
ticksRecord.entity,
ticksRecord.activatedEntity,
ticksRecord.tileEntity
),
usedMemory,
freeMemory,
loadAvg
);
}
}
static class TicksRecord {
final long timed;
final long player;
final long entity;
final long tileEntity;
final long activatedEntity;
TicksRecord() {
timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200);
player = playerTicks;
entity = entityTicks;
tileEntity = tileEntityTicks;
activatedEntity = activatedEntityTicks;
}
}
static class PingRecord {
final double avg;
PingRecord() {
final Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
int totalPing = 0;
for (Player player : onlinePlayers) {
totalPing += player.spigot().getPing();
}
avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size();
}
}
static class Counter {
int count = 0;
@SuppressWarnings({"rawtypes", "SuppressionAnnotation"})
static Function LOADER = new LoadingMap.Feeder<Counter>() {
@Override
public Counter apply() {
return new Counter();
}
};
public int increment() {
return ++count;
}
public int count() {
return count;
}
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import java.util.List;
import static co.aikar.util.JSONUtil.toArrayMapper;
class TimingHistoryEntry {
final TimingData data;
final TimingData[] children;
TimingHistoryEntry(TimingHandler handler) {
this.data = handler.record.clone();
children = new TimingData[handler.children.size()];
int i = 0;
for (TimingData child : handler.children.valueCollection()) {
children[i++] = child.clone();
}
}
List export() {
List result = data.export();
if (children.length > 0) {
result.add(
toArrayMapper(children, new Function<TimingData, Object>() {
@Override
public Object apply(TimingData child) {
return child.export();
}
})
);
}
return result;
}
}

View File

@ -0,0 +1,102 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import co.aikar.util.LoadingMap;
import co.aikar.util.MRUMapCache;
import java.util.ArrayDeque;
import java.util.Map;
/**
* <p>Used as a basis for fast HashMap key comparisons for the Timing Map.</p>
*
* This class uses interned strings giving us the ability to do an identity check instead of equals() on the strings
*/
final class TimingIdentifier {
/**
* Holds all groups. Autoloads on request for a group by name.
*/
static final Map<String, TimingGroup> GROUP_MAP = MRUMapCache.of(
LoadingMap.newIdentityHashMap(new Function<String, TimingGroup>() {
@Override
public TimingGroup apply(String group) {
return new TimingGroup(group);
}
}, 64)
);
static final TimingGroup DEFAULT_GROUP = getGroup("Minecraft");
final String group;
final String name;
final TimingHandler groupHandler;
final boolean protect;
private final int hashCode;
TimingIdentifier(String group, String name, Timing groupHandler, boolean protect) {
this.group = group != null ? group.intern() : DEFAULT_GROUP.name;
this.name = name.intern();
this.groupHandler = groupHandler != null ? groupHandler.getTimingHandler() : null;
this.protect = protect;
this.hashCode = (31 * this.group.hashCode()) + this.name.hashCode();
}
static TimingGroup getGroup(String groupName) {
if (groupName == null) {
return DEFAULT_GROUP;
}
return GROUP_MAP.get(groupName.intern());
}
// We are using .intern() on the strings so it is guaranteed to be an identity comparison.
@SuppressWarnings("StringEquality")
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
TimingIdentifier that = (TimingIdentifier) o;
return group == that.group && name == that.name;
}
@Override
public int hashCode() {
return hashCode;
}
static class TimingGroup {
private static int idPool = 1;
final int id = idPool++;
final String name;
ArrayDeque<TimingHandler> handlers = new ArrayDeque<TimingHandler>(64);
private TimingGroup(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,273 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Preconditions;
import com.google.common.collect.EvictingQueue;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.util.Queue;
import java.util.logging.Level;
@SuppressWarnings("UnusedDeclaration")
public final class Timings {
private static final int MAX_HISTORY_FRAMES = 12;
public static final Timing NULL_HANDLER = new NullTimingHandler();
static boolean timingsEnabled = false;
static boolean verboseEnabled = false;
private static int historyInterval = -1;
private static int historyLength = -1;
private Timings() {}
/**
* Returns a Timing for a plugin corresponding to a name.
*
* @param plugin Plugin to own the Timing
* @param name Name of Timing
* @return Handler
*/
public static Timing of(Plugin plugin, String name) {
Timing pluginHandler = null;
if (plugin != null) {
pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER);
}
return of(plugin, name, pluginHandler);
}
/**
* <p>Returns a handler that has a groupHandler timer handler. Parent timers should not have their
* start/stop methods called directly, as the children will call it for you.</p>
*
* Parent Timers are used to group multiple subsections together and get a summary of them combined
* Parent Handler can not be changed after first call
*
* @param plugin Plugin to own the Timing
* @param name Name of Timing
* @param groupHandler Parent handler to mirror .start/stop calls to
* @return Timing Handler
*/
public static Timing of(Plugin plugin, String name, Timing groupHandler) {
Preconditions.checkNotNull(plugin, "Plugin can not be null");
return TimingsManager.getHandler(plugin.getName(), name, groupHandler, true);
}
/**
* Returns a Timing object after starting it, useful for Java7 try-with-resources.
*
* try (Timing ignored = Timings.ofStart(plugin, someName)) {
* // timed section
* }
*
* @param plugin Plugin to own the Timing
* @param name Name of Timing
* @return Timing Handler
*/
public static Timing ofStart(Plugin plugin, String name) {
return ofStart(plugin, name, null);
}
/**
* Returns a Timing object after starting it, useful for Java7 try-with-resources.
*
* try (Timing ignored = Timings.ofStart(plugin, someName, groupHandler)) {
* // timed section
* }
*
* @param plugin Plugin to own the Timing
* @param name Name of Timing
* @param groupHandler Parent handler to mirror .start/stop calls to
* @return Timing Handler
*/
public static Timing ofStart(Plugin plugin, String name, Timing groupHandler) {
Timing timing = of(plugin, name, groupHandler);
timing.startTimingIfSync();
return timing;
}
/**
* Gets whether or not the Spigot Timings system is enabled
*
* @return Enabled or not
*/
public static boolean isTimingsEnabled() {
return timingsEnabled;
}
/**
* <p>Sets whether or not the Spigot Timings system should be enabled</p>
*
* Calling this will reset timing data.
*
* @param enabled Should timings be reported
*/
public static void setTimingsEnabled(boolean enabled) {
timingsEnabled = enabled;
reset();
}
/**
* <p>Sets whether or not the Timings should monitor at Verbose level.</p>
*
* <p>When Verbose is disabled, high-frequency timings will not be available.</p>
*
* @return Enabled or not
*/
public static boolean isVerboseTimingsEnabled() {
return timingsEnabled;
}
/**
* Sets whether or not the Timings should monitor at Verbose level.
* <p/>
* When Verbose is disabled, high-frequency timings will not be available.
* Calling this will reset timing data.
*
* @param enabled Should high-frequency timings be reported
*/
public static void setVerboseTimingsEnabled(boolean enabled) {
verboseEnabled = enabled;
TimingsManager.needsRecheckEnabled = true;
}
/**
* <p>Gets the interval between Timing History report generation.</p>
*
* Defaults to 5 minutes (6000 ticks)
*
* @return Interval in ticks
*/
public static int getHistoryInterval() {
return historyInterval;
}
/**
* <p>Sets the interval between Timing History report generations.</p>
*
* <p>Defaults to 5 minutes (6000 ticks)</p>
*
* This will recheck your history length, so lowering this value will lower your
* history length if you need more than 60 history windows.
*
* @param interval Interval in ticks
*/
public static void setHistoryInterval(int interval) {
historyInterval = Math.max(20*60, interval);
// Recheck the history length with the new Interval
if (historyLength != -1) {
setHistoryLength(historyLength);
}
}
/**
* Gets how long in ticks Timings history is kept for the server.
*
* Defaults to 1 hour (72000 ticks)
*
* @return Duration in Ticks
*/
public static int getHistoryLength() {
return historyLength;
}
/**
* Sets how long Timing History reports are kept for the server.
*
* Defaults to 1 hours(72000 ticks)
*
* This value is capped at a maximum of getHistoryInterval() * MAX_HISTORY_FRAMES (12)
*
* Will not reset Timing Data but may truncate old history if the new length is less than old length.
*
* @param length Duration in ticks
*/
public static void setHistoryLength(int length) {
// Cap at 12 History Frames, 1 hour at 5 minute frames.
int maxLength = historyInterval * MAX_HISTORY_FRAMES;
// For special cases of servers with special permission to bypass the max.
// This max helps keep data file sizes reasonable for processing on Aikar's Timing parser side.
// Setting this will not help you bypass the max unless Aikar has added an exception on the API side.
if (System.getProperty("timings.bypassMax") != null) {
maxLength = Integer.MAX_VALUE;
}
historyLength = Math.max(Math.min(maxLength, length), historyInterval);
Queue<TimingHistory> oldQueue = TimingsManager.HISTORY;
int frames = (getHistoryLength() / getHistoryInterval());
if (length > maxLength) {
Bukkit.getLogger().log(Level.WARNING, "Timings Length too high. Requested " + length + ", max is " + maxLength + ". To get longer history, you must increase your interval. Set Interval to " + Math.ceil(length / MAX_HISTORY_FRAMES) + " to achieve this length.");
}
TimingsManager.HISTORY = EvictingQueue.create(frames);
TimingsManager.HISTORY.addAll(oldQueue);
}
/**
* Resets all Timing Data
*/
public static void reset() {
TimingsManager.reset();
}
/**
* Generates a report and sends it to the specified command sender.
*
* If sender is null, ConsoleCommandSender will be used.
* @param sender The sender to send to, or null to use the ConsoleCommandSender
*/
public static void generateReport(CommandSender sender) {
if (sender == null) {
sender = Bukkit.getConsoleSender();
}
TimingsExport.reportTimings(sender);
}
/*
=================
Protected API: These are for internal use only in Bukkit/CraftBukkit
These do not have isPrimaryThread() checks in the startTiming/stopTiming
=================
*/
static TimingHandler ofSafe(String name) {
return ofSafe(null, name, null);
}
static Timing ofSafe(Plugin plugin, String name) {
Timing pluginHandler = null;
if (plugin != null) {
pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER);
}
return ofSafe(plugin != null ? plugin.getName() : "Minecraft - Invalid Plugin", name, pluginHandler);
}
static TimingHandler ofSafe(String name, Timing groupHandler) {
return ofSafe(null, name, groupHandler);
}
static TimingHandler ofSafe(String groupName, String name, Timing groupHandler) {
return TimingsManager.getHandler(groupName, name, groupHandler, false);
}
}

View File

@ -0,0 +1,110 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
public class TimingsCommand extends BukkitCommand {
public static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste", "verbon", "verboff");
public TimingsCommand(String name) {
super(name);
this.description = "Manages Spigot Timings data to see performance of the server.";
this.usageMessage = "/timings <reset|report|on|off|verbon|verboff>";
this.setPermission("bukkit.command.timings");
}
@Override
public boolean execute(CommandSender sender, String currentAlias, String[] args) {
if (!testPermission(sender)) {
return true;
}
if (args.length < 1) {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return true;
}
final String arg = args[0];
if ("on".equalsIgnoreCase(arg)) {
Timings.setTimingsEnabled(true);
sender.sendMessage("Enabled Timings & Reset");
return true;
} else if ("off".equalsIgnoreCase(arg)) {
Timings.setTimingsEnabled(false);
sender.sendMessage("Disabled Timings");
return true;
}
if (!Timings.isTimingsEnabled()) {
sender.sendMessage("Please enable timings by typing /timings on");
return true;
}
if ("verbon".equalsIgnoreCase(arg)) {
Timings.setVerboseTimingsEnabled(true);
sender.sendMessage("Enabled Verbose Timings");
return true;
} else if ("verboff".equalsIgnoreCase(arg)) {
Timings.setVerboseTimingsEnabled(false);
sender.sendMessage("Disabled Verbose Timings");
return true;
} else if ("reset".equalsIgnoreCase(arg)) {
TimingsManager.reset();
sender.sendMessage("Timings reset");
} else if ("cost".equals(arg)) {
sender.sendMessage("Timings cost: " + TimingsExport.getCost());
} else if (
"paste".equalsIgnoreCase(arg) ||
"report".equalsIgnoreCase(arg) ||
"get".equalsIgnoreCase(arg) ||
"merged".equalsIgnoreCase(arg) ||
"separate".equalsIgnoreCase(arg)
) {
TimingsExport.reportTimings(sender);
} else {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
}
return true;
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) {
Validate.notNull(sender, "Sender cannot be null");
Validate.notNull(args, "Arguments cannot be null");
Validate.notNull(alias, "Alias cannot be null");
if (args.length == 1) {
return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS,
new ArrayList<String>(TIMINGS_SUBCOMMANDS.size()));
}
return ImmutableList.of();
}
}

View File

@ -0,0 +1,373 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemorySection;
import org.bukkit.entity.EntityType;
import org.bukkit.plugin.Plugin;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import static co.aikar.timings.TimingsManager.HISTORY;
import static co.aikar.util.JSONUtil.*;
@SuppressWarnings({"rawtypes", "SuppressionAnnotation"})
class TimingsExport extends Thread {
private final CommandSender sender;
private final Map out;
private final TimingHistory[] history;
TimingsExport(CommandSender sender, Map out, TimingHistory[] history) {
super("Timings paste thread");
this.sender = sender;
this.out = out;
this.history = history;
}
/**
* Builds an XML report of the timings to be uploaded for parsing.
*
* @param sender Who to report to
*/
static void reportTimings(CommandSender sender) {
Map parent = createObject(
// Get some basic system details about the server
pair("version", Bukkit.getVersion()),
pair("maxplayers", Bukkit.getMaxPlayers()),
pair("start", TimingsManager.timingStart / 1000),
pair("end", System.currentTimeMillis() / 1000),
pair("sampletime", (System.currentTimeMillis() - TimingsManager.timingStart) / 1000)
);
if (!TimingsManager.privacy) {
appendObjectData(parent,
pair("server", Bukkit.getServerName()),
pair("motd", Bukkit.getServer().getMotd()),
pair("online-mode", Bukkit.getServer().getOnlineMode()),
pair("icon", Bukkit.getServer().getServerIcon().getData())
);
}
final Runtime runtime = Runtime.getRuntime();
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
parent.put("system", createObject(
pair("timingcost", getCost()),
pair("name", System.getProperty("os.name")),
pair("version", System.getProperty("os.version")),
pair("jvmversion", System.getProperty("java.version")),
pair("arch", System.getProperty("os.arch")),
pair("maxmem", runtime.maxMemory()),
pair("cpu", runtime.availableProcessors()),
pair("runtime", ManagementFactory.getRuntimeMXBean().getUptime()),
pair("flags", StringUtils.join(runtimeBean.getInputArguments(), " ")),
pair("gc", toObjectMapper(ManagementFactory.getGarbageCollectorMXBeans(), new Function<GarbageCollectorMXBean, JSONPair>() {
@Override
public JSONPair apply(GarbageCollectorMXBean input) {
return pair(input.getName(), toArray(input.getCollectionCount(), input.getCollectionTime()));
}
}))
)
);
Set<Material> tileEntityTypeSet = Sets.newHashSet();
Set<EntityType> entityTypeSet = Sets.newHashSet();
int size = HISTORY.size();
TimingHistory[] history = new TimingHistory[size + 1];
int i = 0;
for (TimingHistory timingHistory : HISTORY) {
tileEntityTypeSet.addAll(timingHistory.tileEntityTypeSet);
entityTypeSet.addAll(timingHistory.entityTypeSet);
history[i++] = timingHistory;
}
history[i] = new TimingHistory(); // Current snapshot
tileEntityTypeSet.addAll(history[i].tileEntityTypeSet);
entityTypeSet.addAll(history[i].entityTypeSet);
Map handlers = createObject();
for (TimingIdentifier.TimingGroup group : TimingIdentifier.GROUP_MAP.values()) {
for (TimingHandler id : group.handlers) {
if (!id.timed && !id.isSpecial()) {
continue;
}
handlers.put(id.id, toArray(
group.id,
id.name
));
}
}
parent.put("idmap", createObject(
pair("groups", toObjectMapper(
TimingIdentifier.GROUP_MAP.values(), new Function<TimingIdentifier.TimingGroup, JSONPair>() {
@Override
public JSONPair apply(TimingIdentifier.TimingGroup group) {
return pair(group.id, group.name);
}
})),
pair("handlers", handlers),
pair("worlds", toObjectMapper(TimingHistory.worldMap.entrySet(), new Function<Map.Entry<String, Integer>, JSONPair>() {
@Override
public JSONPair apply(Map.Entry<String, Integer> input) {
return pair(input.getValue(), input.getKey());
}
})),
pair("tileentity",
toObjectMapper(tileEntityTypeSet, new Function<Material, JSONPair>() {
@Override
public JSONPair apply(Material input) {
return pair(input.getId(), input.name());
}
})),
pair("entity",
toObjectMapper(entityTypeSet, new Function<EntityType, JSONPair>() {
@Override
public JSONPair apply(EntityType input) {
return pair(input.getTypeId(), input.name());
}
}))
));
// Information about loaded plugins
parent.put("plugins", toObjectMapper(Bukkit.getPluginManager().getPlugins(),
new Function<Plugin, JSONPair>() {
@Override
public JSONPair apply(Plugin plugin) {
return pair(plugin.getName(), createObject(
pair("version", plugin.getDescription().getVersion()),
pair("description", String.valueOf(plugin.getDescription().getDescription()).trim()),
pair("website", plugin.getDescription().getWebsite()),
pair("authors", StringUtils.join(plugin.getDescription().getAuthors(), ", "))
));
}
}));
// Information on the users Config
parent.put("config", createObject(
pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)),
pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)),
pair("paperspigot", mapAsJSON(Bukkit.spigot().getPaperSpigotConfig(), null))
));
new TimingsExport(sender, parent, history).start();
}
static long getCost() {
// Benchmark the users System.nanotime() for cost basis
int passes = 100;
TimingHandler SAMPLER1 = Timings.ofSafe("Timings Sampler 1");
TimingHandler SAMPLER2 = Timings.ofSafe("Timings Sampler 2");
TimingHandler SAMPLER3 = Timings.ofSafe("Timings Sampler 3");
TimingHandler SAMPLER4 = Timings.ofSafe("Timings Sampler 4");
TimingHandler SAMPLER5 = Timings.ofSafe("Timings Sampler 5");
TimingHandler SAMPLER6 = Timings.ofSafe("Timings Sampler 6");
long start = System.nanoTime();
for (int i = 0; i < passes; i++) {
SAMPLER1.startTiming();
SAMPLER2.startTiming();
SAMPLER3.startTiming();
SAMPLER3.stopTiming();
SAMPLER4.startTiming();
SAMPLER5.startTiming();
SAMPLER6.startTiming();
SAMPLER6.stopTiming();
SAMPLER5.stopTiming();
SAMPLER4.stopTiming();
SAMPLER2.stopTiming();
SAMPLER1.stopTiming();
}
long timingsCost = (System.nanoTime() - start) / passes / 6;
SAMPLER1.reset(true);
SAMPLER2.reset(true);
SAMPLER3.reset(true);
SAMPLER4.reset(true);
SAMPLER5.reset(true);
SAMPLER6.reset(true);
return timingsCost;
}
private static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) {
JSONObject object = new JSONObject();
for (String key : config.getKeys(false)) {
String fullKey = (parentKey != null ? parentKey + "." + key : key);
if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey)) {
continue;
}
final Object val = config.get(key);
object.put(key, valAsJSON(val, fullKey));
}
return object;
}
private static Object valAsJSON(Object val, final String parentKey) {
if (!(val instanceof MemorySection)) {
if (val instanceof List) {
Iterable<Object> v = (Iterable<Object>) val;
return toArrayMapper(v, new Function<Object, Object>() {
@Override
public Object apply(Object input) {
return valAsJSON(input, parentKey);
}
});
} else {
return val.toString();
}
} else {
return mapAsJSON((ConfigurationSection) val, parentKey);
}
}
@SuppressWarnings("CallToThreadRun")
@Override
public synchronized void start() {
if (sender instanceof RemoteConsoleCommandSender) {
sender.sendMessage(ChatColor.RED + "Warning: Timings report done over RCON will cause lag spikes.");
sender.sendMessage(ChatColor.RED + "You should use " + ChatColor.YELLOW +
"/timings report" + ChatColor.RED + " in game or console.");
run();
} else {
super.start();
}
}
@Override
public void run() {
sender.sendMessage(ChatColor.GREEN + "Preparing Timings Report...");
out.put("data", toArrayMapper(history, new Function<TimingHistory, Object>() {
@Override
public Object apply(TimingHistory input) {
return input.export();
}
}));
String response = null;
try {
HttpURLConnection con = (HttpURLConnection) new URL("http://timings.aikar.co/post").openConnection();
con.setDoOutput(true);
con.setRequestProperty("User-Agent", "Spigot/" + Bukkit.getServerName() + "/" + InetAddress.getLocalHost().getHostName());
con.setRequestMethod("POST");
con.setInstanceFollowRedirects(false);
OutputStream request = new GZIPOutputStream(con.getOutputStream()) {{
this.def.setLevel(7);
}};
request.write(JSONValue.toJSONString(out).getBytes("UTF-8"));
request.close();
response = getResponse(con);
if (con.getResponseCode() != 302) {
sender.sendMessage(
ChatColor.RED + "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage());
sender.sendMessage(ChatColor.RED + "Check your logs for more information");
if (response != null) {
Bukkit.getLogger().log(Level.SEVERE, response);
}
return;
}
String location = con.getHeaderField("Location");
sender.sendMessage(ChatColor.GREEN + "View Timings Report: " + location);
if (!(sender instanceof ConsoleCommandSender)) {
Bukkit.getLogger().log(Level.INFO, "View Timings Report: " + location);
}
if (response != null && !response.isEmpty()) {
Bukkit.getLogger().log(Level.INFO, "Timing Response: " + response);
}
} catch (IOException ex) {
sender.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information");
if (response != null) {
Bukkit.getLogger().log(Level.SEVERE, response);
}
Bukkit.getLogger().log(Level.SEVERE, "Could not paste timings", ex);
}
}
private String getResponse(HttpURLConnection con) throws IOException {
InputStream is = null;
try {
is = con.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(b)) != -1) {
bos.write(b, 0, bytesRead);
}
return bos.toString();
} catch (IOException ex) {
sender.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information");
Bukkit.getLogger().log(Level.WARNING, con.getResponseMessage(), ex);
return null;
} finally {
if (is != null) {
is.close();
}
}
}
}

View File

@ -0,0 +1,194 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Function;
import com.google.common.collect.EvictingQueue;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.PluginClassLoader;
import co.aikar.util.LoadingMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
public final class TimingsManager {
static final Map<TimingIdentifier, TimingHandler> TIMING_MAP =
Collections.synchronizedMap(LoadingMap.newHashMap(
new Function<TimingIdentifier, TimingHandler>() {
@Override
public TimingHandler apply(TimingIdentifier id) {
return (id.protect ?
new UnsafeTimingHandler(id) :
new TimingHandler(id)
);
}
},
256, .5F
));
public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler();
public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK);
public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins");
public static List<String> hiddenConfigs = new ArrayList<String>();
public static boolean privacy = false;
static final Collection<TimingHandler> HANDLERS = new ArrayDeque<TimingHandler>();
static final ArrayDeque<TimingHistory.MinuteReport> MINUTE_REPORTS = new ArrayDeque<TimingHistory.MinuteReport>();
static EvictingQueue<TimingHistory> HISTORY = EvictingQueue.create(12);
static TimingHandler CURRENT;
static long timingStart = 0;
static long historyStart = 0;
static boolean needsFullReset = false;
static boolean needsRecheckEnabled = false;
private TimingsManager() {}
/**
* Resets all timing data on the next tick
*/
static void reset() {
needsFullReset = true;
}
/**
* Ticked every tick by CraftBukkit to count the number of times a timer
* caused TPS loss.
*/
static void tick() {
if (Timings.timingsEnabled) {
boolean violated = FULL_SERVER_TICK.isViolated();
for (TimingHandler handler : HANDLERS) {
if (handler.isSpecial()) {
// We manually call this
continue;
}
handler.processTick(violated);
}
TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size();
TimingHistory.timedTicks++;
// Generate TPS/Ping/Tick reports every minute
}
}
static void stopServer() {
Timings.timingsEnabled = false;
recheckEnabled();
}
static void recheckEnabled() {
synchronized (TIMING_MAP) {
for (TimingHandler timings : TIMING_MAP.values()) {
timings.checkEnabled();
}
}
needsRecheckEnabled = false;
}
static void resetTimings() {
if (needsFullReset) {
// Full resets need to re-check every handlers enabled state
// Timing map can be modified from async so we must sync on it.
synchronized (TIMING_MAP) {
for (TimingHandler timings : TIMING_MAP.values()) {
timings.reset(true);
}
}
Bukkit.getLogger().log(Level.INFO, "Timings Reset");
HISTORY.clear();
needsFullReset = false;
needsRecheckEnabled = false;
timingStart = System.currentTimeMillis();
} else {
// Soft resets only need to act on timings that have done something
// Handlers can only be modified on main thread.
for (TimingHandler timings : HANDLERS) {
timings.reset(false);
}
}
HANDLERS.clear();
MINUTE_REPORTS.clear();
TimingHistory.resetTicks(true);
historyStart = System.currentTimeMillis();
}
static TimingHandler getHandler(String group, String name, Timing parent, boolean protect) {
return TIMING_MAP.get(new TimingIdentifier(group, name, parent, protect));
}
/**
* <p>Due to access restrictions, we need a helper method to get a Command TimingHandler with String group</p>
*
* Plugins should never call this
*
* @param pluginName Plugin this command is associated with
* @param command Command to get timings for
* @return TimingHandler
*/
public static Timing getCommandTiming(String pluginName, Command command) {
Plugin plugin = null;
final Server server = Bukkit.getServer();
if (!("minecraft".equals(pluginName) || "bukkit".equals(pluginName) || "Spigot".equals(pluginName) ||
server == null)) {
plugin = server.getPluginManager().getPlugin(pluginName);
if (plugin == null) {
// Plugin is passing custom fallback prefix, try to look up by class loader
plugin = getPluginByClassloader(command.getClass());
}
}
if (plugin == null) {
return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName());
}
return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName());
}
/**
* Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the
* Plugin that created this class.
*
* @param clazz Class to check
* @return Plugin if created by a plugin
*/
public static Plugin getPluginByClassloader(Class<?> clazz) {
if (clazz == null) {
return null;
}
final ClassLoader classLoader = clazz.getClassLoader();
if (classLoader instanceof PluginClassLoader) {
PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader;
return pluginClassLoader.getPlugin();
}
return null;
}
}

View File

@ -0,0 +1,51 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import org.bukkit.Bukkit;
class UnsafeTimingHandler extends TimingHandler {
UnsafeTimingHandler(TimingIdentifier id) {
super(id);
}
private static void checkThread() {
if (!Bukkit.isPrimaryThread()) {
throw new IllegalStateException("Calling Timings from Async Operation");
}
}
@Override
public void startTiming() {
checkThread();
super.startTiming();
}
@Override
public void stopTiming() {
checkThread();
super.stopTiming();
}
}

View File

@ -0,0 +1,123 @@
package co.aikar.util;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Provides Utility methods that assist with generating JSON Objects
*/
@SuppressWarnings({"rawtypes", "SuppressionAnnotation"})
public final class JSONUtil {
private JSONUtil() {}
/**
* Creates a key/value "JSONPair" object
* @param key
* @param obj
* @return
*/
public static JSONPair pair(String key, Object obj) {
return new JSONPair(key, obj);
}
public static JSONPair pair(long key, Object obj) {
return new JSONPair(String.valueOf(key), obj);
}
/**
* Creates a new JSON object from multiple JsonPair key/value pairs
* @param data
* @return
*/
public static Map createObject(JSONPair... data) {
return appendObjectData(new LinkedHashMap(), data);
}
/**
* This appends multiple key/value Obj pairs into a JSON Object
* @param parent
* @param data
* @return
*/
public static Map appendObjectData(Map parent, JSONPair... data) {
for (JSONPair JSONPair : data) {
parent.put(JSONPair.key, JSONPair.val);
}
return parent;
}
/**
* This builds a JSON array from a set of data
* @param data
* @return
*/
public static List toArray(Object... data) {
return Lists.newArrayList(data);
}
/**
* These help build a single JSON array using a mapper function
* @param collection
* @param mapper
* @param <E>
* @return
*/
public static <E> List toArrayMapper(E[] collection, Function<E, Object> mapper) {
return toArrayMapper(Lists.newArrayList(collection), mapper);
}
public static <E> List toArrayMapper(Iterable<E> collection, Function<E, Object> mapper) {
List array = Lists.newArrayList();
for (E e : collection) {
Object object = mapper.apply(e);
if (object != null) {
array.add(object);
}
}
return array;
}
/**
* These help build a single JSON Object from a collection, using a mapper function
* @param collection
* @param mapper
* @param <E>
* @return
*/
public static <E> Map toObjectMapper(E[] collection, Function<E, JSONPair> mapper) {
return toObjectMapper(Lists.newArrayList(collection), mapper);
}
public static <E> Map toObjectMapper(Iterable<E> collection, Function<E, JSONPair> mapper) {
Map object = Maps.newLinkedHashMap();
for (E e : collection) {
JSONPair JSONPair = mapper.apply(e);
if (JSONPair != null) {
object.put(JSONPair.key, JSONPair.val);
}
}
return object;
}
/**
* Simply stores a key and a value, used internally by many methods below.
*/
@SuppressWarnings("PublicInnerClass")
public static class JSONPair {
final String key;
final Object val;
JSONPair(String key, Object val) {
this.key = key;
this.val = val;
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2015. Starlis LLC / dba Empire Minecraft
*
* This source code is proprietary software and must not be redistributed without Starlis LLC's approval
*
*/
package co.aikar.util;
import com.google.common.base.Function;
import gnu.trove.map.hash.TIntObjectHashMap;
/**
* Allows you to pass a Loader function that when a key is accessed that doesn't exist,
* automatically loads the entry into the map by calling the loader Function.
*
* .get() Will only return null if the Loader can return null.
*
* You may pass any backing Map to use.
*
* This class is not thread safe and should be wrapped with Collections.synchronizedMap on the OUTSIDE of the LoadingMap if needed.
*
* Do not wrap the backing map with Collections.synchronizedMap.
*
* @param <V> Value
*/
public class LoadingIntMap<V> extends TIntObjectHashMap<V> {
private final Function<Integer, V> loader;
/**
* Initializes an auto loading map using specified loader and backing map
* @param loader The loader
*/
public LoadingIntMap(Function<Integer, V> loader) {
this.loader = loader;
}
@Override
public V get(int key) {
V res = super.get(key);
if (res == null) {
res = loader.apply(key);
if (res != null) {
put(key, res);
}
}
return res;
}
/**
* Due to java stuff, you will need to cast it to (Function) for some cases
* @param <T>
*/
public abstract static class Feeder <T> implements Function<T, T> {
@Override
public T apply(Object input) {
return apply();
}
public abstract T apply();
}
}

View File

@ -0,0 +1,332 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.util;
import com.google.common.base.Function;
import org.bukkit.Material;
import co.aikar.timings.TimingHistory;
import org.w3c.dom.css.Counter;
import java.lang.reflect.Constructor;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
/**
* Allows you to pass a Loader function that when a key is accessed that doesn't exists,
* automatically loads the entry into the map by calling the loader Function.
*
* .get() Will only return null if the Loader can return null.
*
* You may pass any backing Map to use.
*
* This class is not thread safe and should be wrapped with Collections.synchronizedMap on the OUTSIDE of the LoadingMap if needed.
*
* Do not wrap the backing map with Collections.synchronizedMap.
*
* @param <K> Key
* @param <V> Value
*/
public class LoadingMap <K,V> extends AbstractMap<K, V> {
private final Map<K, V> backingMap;
private final Function<K, V> loader;
/**
* Initializes an auto loading map using specified loader and backing map
* @param backingMap
* @param loader
*/
public LoadingMap(Map<K, V> backingMap, Function<K, V> loader) {
this.backingMap = backingMap;
this.loader = loader;
}
/**
* Creates a new LoadingMap with the specified map and loader
* @param backingMap
* @param loader
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> of(Map<K, V> backingMap, Function<K, V> loader) {
return new LoadingMap<K, V>(backingMap, loader);
}
/**
* Creates a LoadingMap with an auto instantiating loader.
*
* Will auto construct class of of Value when not found
*
* Since this uses Reflection, It is more effecient to define your own static loader
* than using this helper, but if performance is not critical, this is easier.
*
* @param backingMap Actual map being used.
* @param keyClass Class used for the K generic
* @param valueClass Class used for the V generic
* @param <K> Key Type of the Map
* @param <V> Value Type of the Map
* @return Map that auto instantiates on .get()
*/
public static <K, V> Map<K, V> newAutoMap(Map<K, V> backingMap, final Class<? extends K> keyClass,
final Class<? extends V> valueClass) {
return new LoadingMap<K, V>(backingMap, new AutoInstantiatingLoader<K, V>(keyClass, valueClass));
}
/**
* Creates a LoadingMap with an auto instantiating loader.
*
* Will auto construct class of of Value when not found
*
* Since this uses Reflection, It is more effecient to define your own static loader
* than using this helper, but if performance is not critical, this is easier.
*
* @param backingMap Actual map being used.
* @param valueClass Class used for the V generic
* @param <K> Key Type of the Map
* @param <V> Value Type of the Map
* @return Map that auto instantiates on .get()
*/
public static <K, V> Map<K, V> newAutoMap(Map<K, V> backingMap,
final Class<? extends V> valueClass) {
return newAutoMap(backingMap, null, valueClass);
}
/**
* @see #newAutoMap
*
* new Auto initializing map using a HashMap.
* @param keyClass
* @param valueClass
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashAutoMap(final Class<? extends K> keyClass, final Class<? extends V> valueClass) {
return newAutoMap(new HashMap<K, V>(), keyClass, valueClass);
}
/**
* @see #newAutoMap
*
* new Auto initializing map using a HashMap.
* @param valueClass
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashAutoMap(final Class<? extends V> valueClass) {
return newHashAutoMap(null, valueClass);
}
/**
* @see #newAutoMap
*
* new Auto initializing map using a HashMap.
*
* @param keyClass
* @param valueClass
* @param initialCapacity
* @param loadFactor
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashAutoMap(final Class<? extends K> keyClass, final Class<? extends V> valueClass, int initialCapacity, float loadFactor) {
return newAutoMap(new HashMap<K, V>(initialCapacity, loadFactor), keyClass, valueClass);
}
/**
* @see #newAutoMap
*
* new Auto initializing map using a HashMap.
*
* @param valueClass
* @param initialCapacity
* @param loadFactor
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashAutoMap(final Class<? extends V> valueClass, int initialCapacity, float loadFactor) {
return newHashAutoMap(null, valueClass, initialCapacity, loadFactor);
}
/**
* Initializes an auto loading map using a HashMap
* @param loader
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashMap(Function<K, V> loader) {
return new LoadingMap<K, V>(new HashMap<K, V>(), loader);
}
/**
* Initializes an auto loading map using a HashMap
* @param loader
* @param initialCapacity
* @param loadFactor
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newHashMap(Function<K, V> loader, int initialCapacity, float loadFactor) {
return new LoadingMap<K, V>(new HashMap<K, V>(initialCapacity, loadFactor), loader);
}
/**
* Initializes an auto loading map using an Identity HashMap
* @param loader
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newIdentityHashMap(Function<K, V> loader) {
return new LoadingMap<K, V>(new IdentityHashMap<K, V>(), loader);
}
/**
* Initializes an auto loading map using an Identity HashMap
* @param loader
* @param initialCapacity
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> newIdentityHashMap(Function<K, V> loader, int initialCapacity) {
return new LoadingMap<K, V>(new IdentityHashMap<K, V>(initialCapacity), loader);
}
@Override
public int size() {return backingMap.size();}
@Override
public boolean isEmpty() {return backingMap.isEmpty();}
@Override
public boolean containsKey(Object key) {return backingMap.containsKey(key);}
@Override
public boolean containsValue(Object value) {return backingMap.containsValue(value);}
@Override
public V get(Object key) {
V res = backingMap.get(key);
if (res == null && key != null) {
res = loader.apply((K) key);
if (res != null) {
backingMap.put((K) key, res);
}
}
return res;
}
public V put(K key, V value) {return backingMap.put(key, value);}
@Override
public V remove(Object key) {return backingMap.remove(key);}
public void putAll(Map<? extends K, ? extends V> m) {backingMap.putAll(m);}
@Override
public void clear() {backingMap.clear();}
@Override
public Set<K> keySet() {return backingMap.keySet();}
@Override
public Collection<V> values() {return backingMap.values();}
@Override
public boolean equals(Object o) {return backingMap.equals(o);}
@Override
public int hashCode() {return backingMap.hashCode();}
@Override
public Set<Entry<K, V>> entrySet() {
return backingMap.entrySet();
}
public LoadingMap<K, V> clone() {
return new LoadingMap<K, V>(backingMap, loader);
}
private static class AutoInstantiatingLoader<K, V> implements Function<K, V> {
final Constructor<? extends V> constructor;
private final Class<? extends V> valueClass;
AutoInstantiatingLoader(Class<? extends K> keyClass, Class<? extends V> valueClass) {
try {
this.valueClass = valueClass;
if (keyClass != null) {
constructor = valueClass.getConstructor(keyClass);
} else {
constructor = null;
}
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
valueClass.getName() + " does not have a constructor for " + (keyClass != null ? keyClass.getName() : null));
}
}
@Override
public V apply(K input) {
try {
return (constructor != null ? constructor.newInstance(input) : valueClass.newInstance());
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object object) {
return false;
}
}
/**
* Due to java stuff, you will need to cast it to (Function) for some cases
* @param <T>
*/
public abstract static class Feeder <T> implements Function<T, T> {
@Override
public T apply(Object input) {
return apply();
}
public abstract T apply();
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.util;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Implements a Most Recently Used cache in front of a backing map, to quickly access the last accessed result.
* @param <K>
* @param <V>
*/
public class MRUMapCache<K, V> extends AbstractMap<K, V> {
final Map<K, V> backingMap;
Object cacheKey;
V cacheValue;
public MRUMapCache(final Map<K, V> backingMap) {
this.backingMap = backingMap;
}
public int size() {return backingMap.size();}
public boolean isEmpty() {return backingMap.isEmpty();}
public boolean containsKey(Object key) {
return key != null && key.equals(cacheKey) || backingMap.containsKey(key);
}
public boolean containsValue(Object value) {
return value != null && value == cacheValue || backingMap.containsValue(value);
}
public V get(Object key) {
if (cacheKey != null && cacheKey.equals(key)) {
return cacheValue;
}
cacheKey = key;
return cacheValue = backingMap.get(key);
}
public V put(K key, V value) {
cacheKey = key;
return cacheValue = backingMap.put(key, value);
}
public V remove(Object key) {
if (key != null && key.equals(cacheKey)) {
cacheKey = null;
}
return backingMap.remove(key);
}
public void putAll(Map<? extends K, ? extends V> m) {backingMap.putAll(m);}
public void clear() {
cacheKey = null;
cacheValue = null;
backingMap.clear();
}
public Set<K> keySet() {return backingMap.keySet();}
public Collection<V> values() {return backingMap.values();}
public Set<Map.Entry<K, V>> entrySet() {return backingMap.entrySet();}
/**
* Wraps the specified map with a most recently used cache
* @param map
* @param <K>
* @param <V>
* @return
*/
public static <K, V> Map<K, V> of(Map<K, V> map) {
return new MRUMapCache<K, V>(map);
}
}

View File

@ -0,0 +1,60 @@
package net.techcable.tacospigot.event.entity;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
/**
* Called when an arrow collides with an entity
* <p>
* This event is called <b>before</b> {@link org.bukkit.event.entity.EntityDamageByEntityEvent}, and cancelling it will allow the arrow to continue flying
*/
public class ArrowCollideEvent extends EntityEvent implements Cancellable {
private final Entity collidedWith;
/**
* Get the entity the arrow collided wit
*
* @return the entity
*/
public Entity getCollidedWith() {
return collidedWith;
}
public ArrowCollideEvent(Arrow what, Entity collidedWith) {
super(what);
this.collidedWith = collidedWith;
}
/**
* Get the arrow that collided
*
* @return the arrow that collided
*/
public Arrow getEntity() {
return (Arrow) super.getEntity();
}
private static final HandlerList handlerList = new HandlerList();
public static HandlerList getHandlerList() {
return handlerList;
}
@Override
public HandlerList getHandlers() {
return handlerList;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
}

View File

@ -0,0 +1,71 @@
package net.techcable.tacospigot.event.entity;
import com.google.common.base.Preconditions;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import static com.google.common.base.Preconditions.*;
/**
* Fired before a mob spawns and entity data as calculated.
* <p>This is a more preformat alternative to {@link org.bukkit.event.entity.SpawnerSpawnEvent}</p>
*/
public class SpawnerPreSpawnEvent extends Event implements Cancellable {
private final Location location;
private final EntityType spawnedType;
public SpawnerPreSpawnEvent(Location location, EntityType spawnedType) {
this.location = checkNotNull(location, "Null location").clone(); // Defensive copy
this.spawnedType = checkNotNull(spawnedType, "Null spawned type");
}
/**
* Get the location of the spawner where the entity is being spawned
*
* @return the spawner's location
*/
public Location getLocation() {
return location.clone(); // Defensive copy
}
/**
* Get the type of entity being spawned
*
* @return the type being spawned
*/
public EntityType getSpawnedType() {
return spawnedType;
}
// Cancellable
private boolean cancelled;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
// handlers
private static final HandlerList handlerList = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlerList;
}
public static HandlerList getHandlerList() {
return handlerList;
}
}

View File

@ -0,0 +1,70 @@
package org.bukkit;
/**
* Represents an achievement, which may be given to players.
*/
public enum Achievement {
OPEN_INVENTORY,
MINE_WOOD (OPEN_INVENTORY),
BUILD_WORKBENCH (MINE_WOOD),
BUILD_PICKAXE (BUILD_WORKBENCH),
BUILD_FURNACE (BUILD_PICKAXE),
ACQUIRE_IRON (BUILD_FURNACE),
BUILD_HOE (BUILD_WORKBENCH),
MAKE_BREAD (BUILD_HOE),
BAKE_CAKE (BUILD_HOE),
BUILD_BETTER_PICKAXE (BUILD_PICKAXE),
COOK_FISH (BUILD_FURNACE),
ON_A_RAIL (ACQUIRE_IRON),
BUILD_SWORD (BUILD_WORKBENCH),
KILL_ENEMY (BUILD_SWORD),
KILL_COW (BUILD_SWORD),
FLY_PIG (KILL_COW),
SNIPE_SKELETON (KILL_ENEMY),
GET_DIAMONDS (ACQUIRE_IRON),
NETHER_PORTAL (GET_DIAMONDS),
GHAST_RETURN (NETHER_PORTAL),
GET_BLAZE_ROD (NETHER_PORTAL),
BREW_POTION (GET_BLAZE_ROD),
END_PORTAL (GET_BLAZE_ROD),
THE_END (END_PORTAL),
ENCHANTMENTS (GET_DIAMONDS),
OVERKILL (ENCHANTMENTS),
BOOKCASE (ENCHANTMENTS),
EXPLORE_ALL_BIOMES (END_PORTAL),
SPAWN_WITHER (THE_END),
KILL_WITHER (SPAWN_WITHER),
FULL_BEACON (KILL_WITHER),
BREED_COW (KILL_COW),
DIAMONDS_TO_YOU (GET_DIAMONDS),
OVERPOWERED (BUILD_BETTER_PICKAXE)
;
private final Achievement parent;
private Achievement() {
parent = null;
}
private Achievement(Achievement parent) {
this.parent = parent;
}
/**
* Returns whether or not this achievement has a parent achievement.
*
* @return whether the achievement has a parent achievement
*/
public boolean hasParent() {
return parent != null;
}
/**
* Returns the parent achievement of this achievement, or null if none.
*
* @return the parent achievement or null
*/
public Achievement getParent() {
return parent;
}
}

View File

@ -0,0 +1,111 @@
package org.bukkit;
import java.util.HashMap;
import org.apache.commons.lang.Validate;
import com.google.common.collect.Maps;
/**
* Represents the art on a painting
*/
public enum Art {
KEBAB(0, 1, 1),
AZTEC(1, 1, 1),
ALBAN(2, 1, 1),
AZTEC2(3, 1, 1),
BOMB(4, 1, 1),
PLANT(5, 1, 1),
WASTELAND(6, 1, 1),
POOL(7, 2, 1),
COURBET(8, 2, 1),
SEA(9, 2, 1),
SUNSET(10, 2, 1),
CREEBET(11, 2, 1),
WANDERER(12, 1, 2),
GRAHAM(13, 1, 2),
MATCH(14, 2, 2),
BUST(15, 2, 2),
STAGE(16, 2, 2),
VOID(17, 2, 2),
SKULL_AND_ROSES(18, 2, 2),
WITHER(19, 2, 2),
FIGHTERS(20, 4, 2),
POINTER(21, 4, 4),
PIGSCENE(22, 4, 4),
BURNINGSKULL(23, 4, 4),
SKELETON(24, 4, 3),
DONKEYKONG(25, 4, 3);
private int id, width, height;
private static final HashMap<String, Art> BY_NAME = Maps.newHashMap();
private static final HashMap<Integer, Art> BY_ID = Maps.newHashMap();
private Art(int id, int width, int height) {
this.id = id;
this.width = width;
this.height = height;
}
/**
* Gets the width of the painting, in blocks
*
* @return The width of the painting, in blocks
*/
public int getBlockWidth() {
return width;
}
/**
* Gets the height of the painting, in blocks
*
* @return The height of the painting, in blocks
*/
public int getBlockHeight() {
return height;
}
/**
* Get the ID of this painting.
*
* @return The ID of this painting
* @deprecated Magic value
*/
@Deprecated
public int getId() {
return id;
}
/**
* Get a painting by its numeric ID
*
* @param id The ID
* @return The painting
* @deprecated Magic value
*/
@Deprecated
public static Art getById(int id) {
return BY_ID.get(id);
}
/**
* Get a painting by its unique name
* <p>
* This ignores underscores and capitalization
*
* @param name The name
* @return The painting
*/
public static Art getByName(String name) {
Validate.notNull(name, "Name cannot be null");
return BY_NAME.get(name.toLowerCase().replaceAll("_", ""));
}
static {
for (Art art : values()) {
BY_ID.put(art.id, art);
BY_NAME.put(art.toString().toLowerCase().replaceAll("_", ""), art);
}
}
}

View File

@ -0,0 +1,128 @@
package org.bukkit;
import java.util.Date;
/**
* A single entry from a ban list. This may represent either a player ban or
* an IP ban.
* <p>
* Ban entries include the following properties:
* <table border=1>
* <caption>Property information</caption>
* <tr>
* <th>Property</th>
* <th>Description</th>
* </tr><tr>
* <td>Target Name / IP Address</td>
* <td>The target name or IP address</td>
* </tr><tr>
* <td>Creation Date</td>
* <td>The creation date of the ban</td>
* </tr><tr>
* <td>Source</td>
* <td>The source of the ban, such as a player, console, plugin, etc</td>
* </tr><tr>
* <td>Expiration Date</td>
* <td>The expiration date of the ban</td>
* </tr><tr>
* <td>Reason</td>
* <td>The reason for the ban</td>
* </tr>
* </table>
* <p>
* Unsaved information is not automatically written to the implementation's
* ban list, instead, the {@link #save()} method must be called to write the
* changes to the ban list. If this ban entry has expired (such as from an
* unban) and is no longer found in the list, the {@link #save()} call will
* re-add it to the list, therefore banning the victim specified.
* <p>
* Likewise, changes to the associated {@link BanList} or other entries may or
* may not be reflected in this entry.
*/
public interface BanEntry {
/**
* Gets the target involved. This may be in the form of an IP or a player
* name.
*
* @return the target name or IP address
*/
public String getTarget();
/**
* Gets the date this ban entry was created.
*
* @return the creation date
*/
public Date getCreated();
/**
* Sets the date this ban entry was created.
*
* @param created the new created date, cannot be null
* @see #save() saving changes
*/
public void setCreated(Date created);
/**
* Gets the source of this ban.
* <p>
* Note: A source is considered any String, although this is generally a
* player name.
*
* @return the source of the ban
*/
public String getSource();
/**
* Sets the source of this ban.
* <p>
* Note: A source is considered any String, although this is generally a
* player name.
*
* @param source the new source where null values become empty strings
* @see #save() saving changes
*/
public void setSource(String source);
/**
* Gets the date this ban expires on, or null for no defined end date.
*
* @return the expiration date
*/
public Date getExpiration();
/**
* Sets the date this ban expires on. Null values are considered
* "infinite" bans.
*
* @param expiration the new expiration date, or null to indicate an
* eternity
* @see #save() saving changes
*/
public void setExpiration(Date expiration);
/**
* Gets the reason for this ban.
*
* @return the ban reason, or null if not set
*/
public String getReason();
/**
* Sets the reason for this ban. Reasons must not be null.
*
* @param reason the new reason, null values assume the implementation
* default
* @see #save() saving changes
*/
public void setReason(String reason);
/**
* Saves the ban entry, overwriting any previous data in the ban list.
* <p>
* Saving the ban entry of an unbanned player will cause the player to be
* banned once again.
*/
public void save();
}

View File

@ -0,0 +1,72 @@
package org.bukkit;
import java.util.Date;
import java.util.Set;
/**
* A ban list, containing bans of some {@link Type}.
*/
public interface BanList {
/**
* Represents a ban-type that a {@link BanList} may track.
*/
public enum Type {
/**
* Banned player names
*/
NAME,
/**
* Banned player IP addresses
*/
IP,
;
}
/**
* Gets a {@link BanEntry} by target.
*
* @param target entry parameter to search for
* @return the corresponding entry, or null if none found
*/
public BanEntry getBanEntry(String target);
/**
* Adds a ban to the this list. If a previous ban exists, this will
* update the previous entry.
*
* @param target the target of the ban
* @param reason reason for the ban, null indicates implementation default
* @param expires date for the ban's expiration (unban), or null to imply
* forever
* @param source source of the ban, null indicates implementation default
* @return the entry for the newly created ban, or the entry for the
* (updated) previous ban
*/
public BanEntry addBan(String target, String reason, Date expires, String source);
/**
* Gets a set containing every {@link BanEntry} in this list.
*
* @return an immutable set containing every entry tracked by this list
*/
public Set<BanEntry> getBanEntries();
/**
* Gets if a {@link BanEntry} exists for the target, indicating an active
* ban status.
*
* @param target the target to find
* @return true if a {@link BanEntry} exists for the name, indicating an
* active ban status, false otherwise
*/
public boolean isBanned(String target);
/**
* Removes the specified target from this list, therefore indicating a
* "not banned" status.
*
* @param target the target to remove from this list
*/
public void pardon(String target);
}

View File

@ -0,0 +1,104 @@
package org.bukkit;
/**
* A delegate for handling block changes. This serves as a direct interface
* between generation algorithms in the server implementation and utilizing
* code.
*/
public interface BlockChangeDelegate {
/**
* Set a block type at the specified coordinates without doing all world
* updates and notifications.
* <p>
* It is safe to have this call World.setTypeId, but it may be slower than
* World.setRawTypeId.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @param typeId New block ID
* @return true if the block was set successfully
* @deprecated Magic value
*/
@Deprecated
public boolean setRawTypeId(int x, int y, int z, int typeId);
/**
* Set a block type and data at the specified coordinates without doing
* all world updates and notifications.
* <p>
* It is safe to have this call World.setTypeId, but it may be slower than
* World.setRawTypeId.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @param typeId New block ID
* @param data Block data
* @return true if the block was set successfully
* @deprecated Magic value
*/
@Deprecated
public boolean setRawTypeIdAndData(int x, int y, int z, int typeId, int data);
/**
* Set a block type at the specified coordinates.
* <p>
* This method cannot call World.setRawTypeId, a full update is needed.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @param typeId New block ID
* @return true if the block was set successfully
* @deprecated Magic value
*/
@Deprecated
public boolean setTypeId(int x, int y, int z, int typeId);
/**
* Set a block type and data at the specified coordinates.
* <p>
* This method cannot call World.setRawTypeId, a full update is needed.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @param typeId New block ID
* @param data Block data
* @return true if the block was set successfully
* @deprecated Magic value
*/
@Deprecated
public boolean setTypeIdAndData(int x, int y, int z, int typeId, int data);
/**
* Get the block type at the location.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @return The block ID
* @deprecated Magic value
*/
@Deprecated
public int getTypeId(int x, int y, int z);
/**
* Gets the height of the world.
*
* @return Height of the world
*/
public int getHeight();
/**
* Checks if the specified block is empty (air) or not.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @return True if the block is considered empty.
*/
public boolean isEmpty(int x, int y, int z);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,371 @@
package org.bukkit;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.lang.Validate;
import com.google.common.collect.Maps;
/**
* All supported color values for chat
*/
public enum ChatColor{
/**
* Represents black
*/
BLACK('0', 0x00) {
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.BLACK;
}
},
/**
* Represents dark blue
*/
DARK_BLUE('1', 0x1){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_BLUE;
}
},
/**
* Represents dark green
*/
DARK_GREEN('2', 0x2){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_GREEN;
}
},
/**
* Represents dark blue (aqua)
*/
DARK_AQUA('3', 0x3){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_AQUA;
}
},
/**
* Represents dark red
*/
DARK_RED('4', 0x4){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_RED;
}
},
/**
* Represents dark purple
*/
DARK_PURPLE('5', 0x5){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_PURPLE;
}
},
/**
* Represents gold
*/
GOLD('6', 0x6){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.GOLD;
}
},
/**
* Represents gray
*/
GRAY('7', 0x7){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.GRAY;
}
},
/**
* Represents dark gray
*/
DARK_GRAY('8', 0x8){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.DARK_GRAY;
}
},
/**
* Represents blue
*/
BLUE('9', 0x9){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.BLUE;
}
},
/**
* Represents green
*/
GREEN('a', 0xA){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.GREEN;
}
},
/**
* Represents aqua
*/
AQUA('b', 0xB){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.AQUA;
}
},
/**
* Represents red
*/
RED('c', 0xC){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.RED;
}
},
/**
* Represents light purple
*/
LIGHT_PURPLE('d', 0xD){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.LIGHT_PURPLE;
}
},
/**
* Represents yellow
*/
YELLOW('e', 0xE){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.YELLOW;
}
},
/**
* Represents white
*/
WHITE('f', 0xF){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.WHITE;
}
},
/**
* Represents magical characters that change around randomly
*/
MAGIC('k', 0x10, true){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.MAGIC;
}
},
/**
* Makes the text bold.
*/
BOLD('l', 0x11, true){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.BOLD;
}
},
/**
* Makes a line appear through the text.
*/
STRIKETHROUGH('m', 0x12, true){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.STRIKETHROUGH;
}
},
/**
* Makes the text appear underlined.
*/
UNDERLINE('n', 0x13, true){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.UNDERLINE;
}
},
/**
* Makes the text italic.
*/
ITALIC('o', 0x14, true){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.ITALIC;
}
},
/**
* Resets all previous chat colors or formats.
*/
RESET('r', 0x15){
@Override
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.RESET;
}
};
/**
* The special character which prefixes all chat colour codes. Use this if
* you need to dynamically convert colour codes from your custom format.
*/
public static final char COLOR_CHAR = '\u00A7';
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]");
private final int intCode;
private final char code;
private final boolean isFormat;
private final String toString;
private final static Map<Integer, ChatColor> BY_ID = Maps.newHashMap();
private final static Map<Character, ChatColor> BY_CHAR = Maps.newHashMap();
private ChatColor(char code, int intCode) {
this(code, intCode, false);
}
private ChatColor(char code, int intCode, boolean isFormat) {
this.code = code;
this.intCode = intCode;
this.isFormat = isFormat;
this.toString = new String(new char[] {COLOR_CHAR, code});
}
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.RESET;
};
/**
* Gets the char value associated with this color
*
* @return A char value of this color code
*/
public char getChar() {
return code;
}
@Override
public String toString() {
return toString;
}
/**
* Checks if this code is a format code as opposed to a color code.
*
* @return whether this ChatColor is a format code
*/
public boolean isFormat() {
return isFormat;
}
/**
* Checks if this code is a color code as opposed to a format code.
*
* @return whether this ChatColor is a color code
*/
public boolean isColor() {
return !isFormat && this != RESET;
}
/**
* Gets the color represented by the specified color code
*
* @param code Code to check
* @return Associative {@link org.bukkit.ChatColor} with the given code,
* or null if it doesn't exist
*/
public static ChatColor getByChar(char code) {
return BY_CHAR.get(code);
}
/**
* Gets the color represented by the specified color code
*
* @param code Code to check
* @return Associative {@link org.bukkit.ChatColor} with the given code,
* or null if it doesn't exist
*/
public static ChatColor getByChar(String code) {
Validate.notNull(code, "Code cannot be null");
Validate.isTrue(code.length() > 0, "Code must have at least one char");
return BY_CHAR.get(code.charAt(0));
}
/**
* Strips the given message of all color codes
*
* @param input String to strip of color
* @return A copy of the input string, without any coloring
*/
public static String stripColor(final String input) {
if (input == null) {
return null;
}
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
}
/**
* Translates a string using an alternate color code character into a
* string that uses the internal ChatColor.COLOR_CODE color code
* character. The alternate color code character will only be replaced if
* it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r.
*
* @param altColorChar The alternate color code character to replace. Ex: {@literal &}
* @param textToTranslate Text containing the alternate color code character.
* @return Text containing the ChatColor.COLOR_CODE color code character.
*/
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
char[] b = textToTranslate.toCharArray();
for (int i = 0; i < b.length - 1; i++) {
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) {
b[i] = ChatColor.COLOR_CHAR;
b[i+1] = Character.toLowerCase(b[i+1]);
}
}
return new String(b);
}
/**
* Gets the ChatColors used at the end of the given input string.
*
* @param input Input string to retrieve the colors from.
* @return Any remaining ChatColors to pass onto the next line.
*/
public static String getLastColors(String input) {
String result = "";
int length = input.length();
// Search backwards from the end as it is faster
for (int index = length - 1; index > -1; index--) {
char section = input.charAt(index);
if (section == COLOR_CHAR && index < length - 1) {
char c = input.charAt(index + 1);
ChatColor color = getByChar(c);
if (color != null) {
result = color.toString() + result;
// Once we find a color or reset we can stop searching
if (color.isColor() || color.equals(RESET)) {
break;
}
}
}
}
return result;
}
static {
for (ChatColor color : values()) {
BY_ID.put(color.intCode, color);
BY_CHAR.put(color.code, color);
}
}
}

View File

@ -0,0 +1,124 @@
package org.bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
/**
* Represents a chunk of blocks
*/
public interface Chunk {
/**
* Gets the X-coordinate of this chunk
*
* @return X-coordinate
*/
int getX();
/**
* Gets the Z-coordinate of this chunk
*
* @return Z-coordinate
*/
int getZ();
/**
* Gets the world containing this chunk
*
* @return Parent World
*/
World getWorld();
/**
* Gets a block from this chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return the Block
*/
Block getBlock(int x, int y, int z);
/**
* Capture thread-safe read-only snapshot of chunk data
*
* @return ChunkSnapshot
*/
ChunkSnapshot getChunkSnapshot();
/**
* Capture thread-safe read-only snapshot of chunk data
*
* @param includeMaxblocky - if true, snapshot includes per-coordinate
* maximum Y values
* @param includeBiome - if true, snapshot includes per-coordinate biome
* type
* @param includeBiomeTempRain - if true, snapshot includes per-coordinate
* raw biome temperature and rainfall
* @return ChunkSnapshot
*/
ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain);
/**
* Get a list of all entities in the chunk.
*
* @return The entities.
*/
Entity[] getEntities();
/**
* Get a list of all tile entities in the chunk.
*
* @return The tile entities.
*/
BlockState[] getTileEntities();
/**
* Checks if the chunk is loaded.
*
* @return True if it is loaded.
*/
boolean isLoaded();
/**
* Loads the chunk.
*
* @param generate Whether or not to generate a chunk if it doesn't
* already exist
* @return true if the chunk has loaded successfully, otherwise false
*/
boolean load(boolean generate);
/**
* Loads the chunk.
*
* @return true if the chunk has loaded successfully, otherwise false
*/
boolean load();
/**
* Unloads and optionally saves the Chunk
*
* @param save Controls whether the chunk is saved
* @param safe Controls whether to unload the chunk when players are
* nearby
* @return true if the chunk has unloaded successfully, otherwise false
*/
boolean unload(boolean save, boolean safe);
/**
* Unloads and optionally saves the Chunk
*
* @param save Controls whether the chunk is saved
* @return true if the chunk has unloaded successfully, otherwise false
*/
boolean unload(boolean save);
/**
* Unloads and optionally saves the Chunk
*
* @return true if the chunk has unloaded successfully, otherwise false
*/
boolean unload();
}

View File

@ -0,0 +1,129 @@
package org.bukkit;
import org.bukkit.block.Biome;
/**
* Represents a static, thread-safe snapshot of chunk of blocks.
* <p>
* Purpose is to allow clean, efficient copy of a chunk data to be made, and
* then handed off for processing in another thread (e.g. map rendering)
*/
public interface ChunkSnapshot {
/**
* Gets the X-coordinate of this chunk
*
* @return X-coordinate
*/
int getX();
/**
* Gets the Z-coordinate of this chunk
*
* @return Z-coordinate
*/
int getZ();
/**
* Gets name of the world containing this chunk
*
* @return Parent World Name
*/
String getWorldName();
/**
* Get block type for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-255
* @deprecated Magic value
*/
@Deprecated
int getBlockTypeId(int x, int y, int z);
/**
* Get block data for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
* @deprecated Magic value
*/
@Deprecated
int getBlockData(int x, int y, int z);
/**
* Get sky light level for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
*/
int getBlockSkyLight(int x, int y, int z);
/**
* Get light level emitted by block at corresponding coordinate in the
* chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
*/
int getBlockEmittedLight(int x, int y, int z);
/**
* Gets the highest non-air coordinate at the given coordinates
*
* @param x X-coordinate of the blocks
* @param z Z-coordinate of the blocks
* @return Y-coordinate of the highest non-air block
*/
int getHighestBlockYAt(int x, int z);
/**
* Get biome at given coordinates
*
* @param x X-coordinate
* @param z Z-coordinate
* @return Biome at given coordinate
*/
Biome getBiome(int x, int z);
/**
* Get raw biome temperature (0.0-1.0) at given coordinate
*
* @param x X-coordinate
* @param z Z-coordinate
* @return temperature at given coordinate
*/
double getRawBiomeTemperature(int x, int z);
/**
* Get raw biome rainfall (0.0-1.0) at given coordinate
*
* @param x X-coordinate
* @param z Z-coordinate
* @return rainfall at given coordinate
*/
double getRawBiomeRainfall(int x, int z);
/**
* Get world full time when chunk snapshot was captured
*
* @return time in ticks
*/
long getCaptureFullTime();
/**
* Test if section is empty
*
* @param sy - section Y coordinate (block Y / 16)
* @return true if empty, false if not
*/
boolean isSectionEmpty(int sy);
}

View File

@ -0,0 +1,50 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the two types of coal
*/
public enum CoalType {
COAL(0x0),
CHARCOAL(0x1);
private final byte data;
private final static Map<Byte, CoalType> BY_DATA = Maps.newHashMap();
private CoalType(final int data) {
this.data = (byte) data;
}
/**
* Gets the associated data value representing this type of coal
*
* @return A byte containing the data value of this coal type
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the type of coal with the given data value
*
* @param data Data value to fetch
* @return The {@link CoalType} representing the given value, or null if
* it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static CoalType getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (CoalType type : values()) {
BY_DATA.put(type.data, type);
}
}
}

View File

@ -0,0 +1,344 @@
package org.bukkit;
import java.util.Map;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import com.google.common.collect.ImmutableMap;
/**
* A container for a color palette. This class is immutable; the set methods
* return a new color. The color names listed as fields are HTML4 standards,
* but subject to change.
*/
@SerializableAs("Color")
public final class Color implements ConfigurationSerializable {
private static final int BIT_MASK = 0xff;
/**
* White, or (0xFF,0xFF,0xFF) in (R,G,B)
*/
public static final Color WHITE = fromRGB(0xFFFFFF);
/**
* Silver, or (0xC0,0xC0,0xC0) in (R,G,B)
*/
public static final Color SILVER = fromRGB(0xC0C0C0);
/**
* Gray, or (0x80,0x80,0x80) in (R,G,B)
*/
public static final Color GRAY = fromRGB(0x808080);
/**
* Black, or (0x00,0x00,0x00) in (R,G,B)
*/
public static final Color BLACK = fromRGB(0x000000);
/**
* Red, or (0xFF,0x00,0x00) in (R,G,B)
*/
public static final Color RED = fromRGB(0xFF0000);
/**
* Maroon, or (0x80,0x00,0x00) in (R,G,B)
*/
public static final Color MAROON = fromRGB(0x800000);
/**
* Yellow, or (0xFF,0xFF,0x00) in (R,G,B)
*/
public static final Color YELLOW = fromRGB(0xFFFF00);
/**
* Olive, or (0x80,0x80,0x00) in (R,G,B)
*/
public static final Color OLIVE = fromRGB(0x808000);
/**
* Lime, or (0x00,0xFF,0x00) in (R,G,B)
*/
public static final Color LIME = fromRGB(0x00FF00);
/**
* Green, or (0x00,0x80,0x00) in (R,G,B)
*/
public static final Color GREEN = fromRGB(0x008000);
/**
* Aqua, or (0x00,0xFF,0xFF) in (R,G,B)
*/
public static final Color AQUA = fromRGB(0x00FFFF);
/**
* Teal, or (0x00,0x80,0x80) in (R,G,B)
*/
public static final Color TEAL = fromRGB(0x008080);
/**
* Blue, or (0x00,0x00,0xFF) in (R,G,B)
*/
public static final Color BLUE = fromRGB(0x0000FF);
/**
* Navy, or (0x00,0x00,0x80) in (R,G,B)
*/
public static final Color NAVY = fromRGB(0x000080);
/**
* Fuchsia, or (0xFF,0x00,0xFF) in (R,G,B)
*/
public static final Color FUCHSIA = fromRGB(0xFF00FF);
/**
* Purple, or (0x80,0x00,0x80) in (R,G,B)
*/
public static final Color PURPLE = fromRGB(0x800080);
/**
* Orange, or (0xFF,0xA5,0x00) in (R,G,B)
*/
public static final Color ORANGE = fromRGB(0xFFA500);
private final byte red;
private final byte green;
private final byte blue;
/**
* Creates a new Color object from a red, green, and blue
*
* @param red integer from 0-255
* @param green integer from 0-255
* @param blue integer from 0-255
* @return a new Color object for the red, green, blue
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
*/
public static Color fromRGB(int red, int green, int blue) throws IllegalArgumentException {
return new Color(red, green, blue);
}
/**
* Creates a new Color object from a blue, green, and red
*
* @param blue integer from 0-255
* @param green integer from 0-255
* @param red integer from 0-255
* @return a new Color object for the red, green, blue
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
*/
public static Color fromBGR(int blue, int green, int red) throws IllegalArgumentException {
return new Color(red, green, blue);
}
/**
* Creates a new color object from an integer that contains the red,
* green, and blue bytes in the lowest order 24 bits.
*
* @param rgb the integer storing the red, green, and blue values
* @return a new color object for specified values
* @throws IllegalArgumentException if any data is in the highest order 8
* bits
*/
public static Color fromRGB(int rgb) throws IllegalArgumentException {
Validate.isTrue((rgb >> 24) == 0, "Extrenuous data in: ", rgb);
return fromRGB(rgb >> 16 & BIT_MASK, rgb >> 8 & BIT_MASK, rgb >> 0 & BIT_MASK);
}
/**
* Creates a new color object from an integer that contains the blue,
* green, and red bytes in the lowest order 24 bits.
*
* @param bgr the integer storing the blue, green, and red values
* @return a new color object for specified values
* @throws IllegalArgumentException if any data is in the highest order 8
* bits
*/
public static Color fromBGR(int bgr) throws IllegalArgumentException {
Validate.isTrue((bgr >> 24) == 0, "Extrenuous data in: ", bgr);
return fromBGR(bgr >> 16 & BIT_MASK, bgr >> 8 & BIT_MASK, bgr >> 0 & BIT_MASK);
}
private Color(int red, int green, int blue) {
Validate.isTrue(red >= 0 && red <= BIT_MASK, "Red is not between 0-255: ", red);
Validate.isTrue(green >= 0 && green <= BIT_MASK, "Green is not between 0-255: ", green);
Validate.isTrue(blue >= 0 && blue <= BIT_MASK, "Blue is not between 0-255: ", blue);
this.red = (byte) red;
this.green = (byte) green;
this.blue = (byte) blue;
}
/**
* Gets the red component
*
* @return red component, from 0 to 255
*/
public int getRed() {
return BIT_MASK & red;
}
/**
* Creates a new Color object with specified component
*
* @param red the red component, from 0 to 255
* @return a new color object with the red component
*/
public Color setRed(int red) {
return fromRGB(red, getGreen(), getBlue());
}
/**
* Gets the green component
*
* @return green component, from 0 to 255
*/
public int getGreen() {
return BIT_MASK & green;
}
/**
* Creates a new Color object with specified component
*
* @param green the red component, from 0 to 255
* @return a new color object with the red component
*/
public Color setGreen(int green) {
return fromRGB(getRed(), green, getBlue());
}
/**
* Gets the blue component
*
* @return blue component, from 0 to 255
*/
public int getBlue() {
return BIT_MASK & blue;
}
/**
* Creates a new Color object with specified component
*
* @param blue the red component, from 0 to 255
* @return a new color object with the red component
*/
public Color setBlue(int blue) {
return fromRGB(getRed(), getGreen(), blue);
}
/**
*
* @return An integer representation of this color, as 0xRRGGBB
*/
public int asRGB() {
return getRed() << 16 | getGreen() << 8 | getBlue() << 0;
}
/**
*
* @return An integer representation of this color, as 0xBBGGRR
*/
public int asBGR() {
return getBlue() << 16 | getGreen() << 8 | getRed() << 0;
}
/**
* Creates a new color with its RGB components changed as if it was dyed
* with the colors passed in, replicating vanilla workbench dyeing
*
* @param colors The DyeColors to dye with
* @return A new color with the changed rgb components
*/
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
public Color mixDyes(DyeColor... colors) {
Validate.noNullElements(colors, "Colors cannot be null");
Color[] toPass = new Color[colors.length];
for (int i = 0; i < colors.length; i++) {
toPass[i] = colors[i].getColor();
}
return mixColors(toPass);
}
/**
* Creates a new color with its RGB components changed as if it was dyed
* with the colors passed in, replicating vanilla workbench dyeing
*
* @param colors The colors to dye with
* @return A new color with the changed rgb components
*/
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
public Color mixColors(Color... colors) {
Validate.noNullElements(colors, "Colors cannot be null");
int totalRed = this.getRed();
int totalGreen = this.getGreen();
int totalBlue = this.getBlue();
int totalMax = Math.max(Math.max(totalRed, totalGreen), totalBlue);
for (Color color : colors) {
totalRed += color.getRed();
totalGreen += color.getGreen();
totalBlue += color.getBlue();
totalMax += Math.max(Math.max(color.getRed(), color.getGreen()), color.getBlue());
}
float averageRed = totalRed / (colors.length + 1);
float averageGreen = totalGreen / (colors.length + 1);
float averageBlue = totalBlue / (colors.length + 1);
float averageMax = totalMax / (colors.length + 1);
float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
float gainFactor = averageMax / maximumOfAverages;
return Color.fromRGB((int) (averageRed * gainFactor), (int) (averageGreen * gainFactor), (int) (averageBlue * gainFactor));
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Color)) {
return false;
}
final Color that = (Color) o;
return this.blue == that.blue && this.green == that.green && this.red == that.red;
}
@Override
public int hashCode() {
return asRGB() ^ Color.class.hashCode();
}
public Map<String, Object> serialize() {
return ImmutableMap.<String, Object>of(
"RED", getRed(),
"BLUE", getBlue(),
"GREEN", getGreen()
);
}
@SuppressWarnings("javadoc")
public static Color deserialize(Map<String, Object> map) {
return fromRGB(
asInt("RED", map),
asInt("GREEN", map),
asInt("BLUE", map)
);
}
private static int asInt(String string, Map<String, Object> map) {
Object value = map.get(string);
if (value == null) {
throw new IllegalArgumentException(string + " not in map " + map);
}
if (!(value instanceof Number)) {
throw new IllegalArgumentException(string + '(' + value + ") is not a number");
}
return ((Number) value).intValue();
}
@Override
public String toString() {
return "Color:[rgb0x" + Integer.toHexString(getRed()).toUpperCase() + Integer.toHexString(getGreen()).toUpperCase() + Integer.toHexString(getBlue()).toUpperCase() + "]";
}
}

View File

@ -0,0 +1,81 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the different growth states of crops
*/
public enum CropState {
/**
* State when first seeded
*/
SEEDED(0x0),
/**
* First growth stage
*/
GERMINATED(0x1),
/**
* Second growth stage
*/
VERY_SMALL(0x2),
/**
* Third growth stage
*/
SMALL(0x3),
/**
* Fourth growth stage
*/
MEDIUM(0x4),
/**
* Fifth growth stage
*/
TALL(0x5),
/**
* Almost ripe stage
*/
VERY_TALL(0x6),
/**
* Ripe stage
*/
RIPE(0x7);
private final byte data;
private final static Map<Byte, CropState> BY_DATA = Maps.newHashMap();
private CropState(final int data) {
this.data = (byte) data;
}
/**
* Gets the associated data value representing this growth state
*
* @return A byte containing the data value of this growth state
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the CropState with the given data value
*
* @param data Data value to fetch
* @return The {@link CropState} representing the given value, or null if
* it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static CropState getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (CropState cropState : values()) {
BY_DATA.put(cropState.getData(), cropState);
}
}
}

View File

@ -0,0 +1,72 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the various difficulty levels that are available.
*/
public enum Difficulty {
/**
* Players regain health over time, hostile mobs don't spawn, the hunger
* bar does not deplete.
*/
PEACEFUL(0),
/**
* Hostile mobs spawn, enemies deal less damage than on normal difficulty,
* the hunger bar does deplete and starving deals up to 5 hearts of
* damage. (Default value)
*/
EASY(1),
/**
* Hostile mobs spawn, enemies deal normal amounts of damage, the hunger
* bar does deplete and starving deals up to 9.5 hearts of damage.
*/
NORMAL(2),
/**
* Hostile mobs spawn, enemies deal greater damage than on normal
* difficulty, the hunger bar does deplete and starving can kill players.
*/
HARD(3);
private final int value;
private final static Map<Integer, Difficulty> BY_ID = Maps.newHashMap();
private Difficulty(final int value) {
this.value = value;
}
/**
* Gets the difficulty value associated with this Difficulty.
*
* @return An integer value of this difficulty
* @deprecated Magic value
*/
@Deprecated
public int getValue() {
return value;
}
/**
* Gets the Difficulty represented by the specified value
*
* @param value Value to check
* @return Associative {@link Difficulty} with the given value, or null if
* it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static Difficulty getByValue(final int value) {
return BY_ID.get(value);
}
static {
for (Difficulty diff : values()) {
BY_ID.put(diff.value, diff);
}
}
}

View File

@ -0,0 +1,239 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
/**
* All supported color values for dyes and cloth
*/
public enum DyeColor {
/**
* Represents white dye.
*/
WHITE(0x0, 0xF, Color.WHITE, Color.fromRGB(0xF0F0F0)),
/**
* Represents orange dye.
*/
ORANGE(0x1, 0xE, Color.fromRGB(0xD87F33), Color.fromRGB(0xEB8844)),
/**
* Represents magenta dye.
*/
MAGENTA(0x2, 0xD, Color.fromRGB(0xB24CD8), Color.fromRGB(0xC354CD)),
/**
* Represents light blue dye.
*/
LIGHT_BLUE(0x3, 0xC, Color.fromRGB(0x6699D8), Color.fromRGB(0x6689D3)),
/**
* Represents yellow dye.
*/
YELLOW(0x4, 0xB, Color.fromRGB(0xE5E533), Color.fromRGB(0xDECF2A)),
/**
* Represents lime dye.
*/
LIME(0x5, 0xA, Color.fromRGB(0x7FCC19), Color.fromRGB(0x41CD34)),
/**
* Represents pink dye.
*/
PINK(0x6, 0x9, Color.fromRGB(0xF27FA5), Color.fromRGB(0xD88198)),
/**
* Represents gray dye.
*/
GRAY(0x7, 0x8, Color.fromRGB(0x4C4C4C), Color.fromRGB(0x434343)),
/**
* Represents silver dye.
*/
SILVER(0x8, 0x7, Color.fromRGB(0x999999), Color.fromRGB(0xABABAB)),
/**
* Represents cyan dye.
*/
CYAN(0x9, 0x6, Color.fromRGB(0x4C7F99), Color.fromRGB(0x287697)),
/**
* Represents purple dye.
*/
PURPLE(0xA, 0x5, Color.fromRGB(0x7F3FB2), Color.fromRGB(0x7B2FBE)),
/**
* Represents blue dye.
*/
BLUE(0xB, 0x4, Color.fromRGB(0x334CB2), Color.fromRGB(0x253192)),
/**
* Represents brown dye.
*/
BROWN(0xC, 0x3, Color.fromRGB(0x664C33), Color.fromRGB(0x51301A)),
/**
* Represents green dye.
*/
GREEN(0xD, 0x2, Color.fromRGB(0x667F33), Color.fromRGB(0x3B511A)),
/**
* Represents red dye.
*/
RED(0xE, 0x1, Color.fromRGB(0x993333), Color.fromRGB(0xB3312C)),
/**
* Represents black dye.
*/
BLACK(0xF, 0x0, Color.fromRGB(0x191919), Color.fromRGB(0x1E1B1B));
private final byte woolData;
private final byte dyeData;
private final Color color;
private final Color firework;
private final static DyeColor[] BY_WOOL_DATA;
private final static DyeColor[] BY_DYE_DATA;
private final static Map<Color, DyeColor> BY_COLOR;
private final static Map<Color, DyeColor> BY_FIREWORK;
private DyeColor(final int woolData, final int dyeData, Color color, Color firework) {
this.woolData = (byte) woolData;
this.dyeData = (byte) dyeData;
this.color = color;
this.firework = firework;
}
/**
* Gets the associated (wool) data value representing this color.
*
* @return A byte containing the (wool) data value of this color
* @deprecated The name is misleading. It would imply {@link
* Material#INK_SACK} but uses {@link Material#WOOL}
* @see #getWoolData()
* @see #getDyeData()
*/
@Deprecated
public byte getData() {
return getWoolData();
}
/**
* Gets the associated wool data value representing this color.
*
* @return A byte containing the wool data value of this color
* @see #getDyeData()
* @deprecated Magic value
*/
@Deprecated
public byte getWoolData() {
return woolData;
}
/**
* Gets the associated dye data value representing this color.
*
* @return A byte containing the dye data value of this color
* @see #getWoolData()
* @deprecated Magic value
*/
@Deprecated
public byte getDyeData() {
return dyeData;
}
/**
* Gets the color that this dye represents.
*
* @return The {@link Color} that this dye represents
*/
public Color getColor() {
return color;
}
/**
* Gets the firework color that this dye represents.
*
* @return The {@link Color} that this dye represents
*/
public Color getFireworkColor() {
return firework;
}
/**
* Gets the DyeColor with the given (wool) data value.
*
* @param data (wool) data value to fetch
* @return The {@link DyeColor} representing the given value, or null if
* it doesn't exist
* @deprecated The name is misleading. It would imply {@link
* Material#INK_SACK} but uses {@link Material#WOOL}
* @see #getByDyeData(byte)
* @see #getByWoolData(byte)
*/
@Deprecated
public static DyeColor getByData(final byte data) {
return getByWoolData(data);
}
/**
* Gets the DyeColor with the given wool data value.
*
* @param data Wool data value to fetch
* @return The {@link DyeColor} representing the given value, or null if
* it doesn't exist
* @see #getByDyeData(byte)
* @deprecated Magic value
*/
@Deprecated
public static DyeColor getByWoolData(final byte data) {
int i = 0xff & data;
if (i >= BY_WOOL_DATA.length) {
return null;
}
return BY_WOOL_DATA[i];
}
/**
* Gets the DyeColor with the given dye data value.
*
* @param data Dye data value to fetch
* @return The {@link DyeColor} representing the given value, or null if
* it doesn't exist
* @see #getByWoolData(byte)
* @deprecated Magic value
*/
@Deprecated
public static DyeColor getByDyeData(final byte data) {
int i = 0xff & data;
if (i >= BY_DYE_DATA.length) {
return null;
}
return BY_DYE_DATA[i];
}
/**
* Gets the DyeColor with the given color value.
*
* @param color Color value to get the dye by
* @return The {@link DyeColor} representing the given value, or null if
* it doesn't exist
*/
public static DyeColor getByColor(final Color color) {
return BY_COLOR.get(color);
}
/**
* Gets the DyeColor with the given firework color value.
*
* @param color Color value to get dye by
* @return The {@link DyeColor} representing the given value, or null if
* it doesn't exist
*/
public static DyeColor getByFireworkColor(final Color color) {
return BY_FIREWORK.get(color);
}
static {
BY_WOOL_DATA = values();
BY_DYE_DATA = values();
ImmutableMap.Builder<Color, DyeColor> byColor = ImmutableMap.builder();
ImmutableMap.Builder<Color, DyeColor> byFirework = ImmutableMap.builder();
for (DyeColor color : values()) {
BY_WOOL_DATA[color.woolData & 0xff] = color;
BY_DYE_DATA[color.dyeData & 0xff] = color;
byColor.put(color.getColor(), color);
byFirework.put(color.getFireworkColor(), color);
}
BY_COLOR = byColor.build();
BY_FIREWORK = byFirework.build();
}
}

View File

@ -0,0 +1,337 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
import org.bukkit.block.BlockFace;
import org.bukkit.material.MaterialData;
import org.bukkit.potion.Potion;
/**
* A list of effects that the server is able to send to players.
*/
public enum Effect {
/**
* An alternate click sound.
*/
CLICK2(1000, Type.SOUND),
/**
* A click sound.
*/
CLICK1(1001, Type.SOUND),
/**
* Sound of a bow firing.
*/
BOW_FIRE(1002, Type.SOUND),
/**
* Sound of a door opening/closing.
*/
DOOR_TOGGLE(1003, Type.SOUND),
/**
* Sound of fire being extinguished.
*/
EXTINGUISH(1004, Type.SOUND),
/**
* A song from a record. Needs the record item ID as additional info
*/
RECORD_PLAY(1005, Type.SOUND, Material.class),
/**
* Sound of ghast shrieking.
*/
GHAST_SHRIEK(1007, Type.SOUND),
/**
* Sound of ghast firing.
*/
GHAST_SHOOT(1008, Type.SOUND),
/**
* Sound of blaze firing.
*/
BLAZE_SHOOT(1009, Type.SOUND),
/**
* Sound of zombies chewing on wooden doors.
*/
ZOMBIE_CHEW_WOODEN_DOOR(1010, Type.SOUND),
/**
* Sound of zombies chewing on iron doors.
*/
ZOMBIE_CHEW_IRON_DOOR(1011, Type.SOUND),
/**
* Sound of zombies destroying a door.
*/
ZOMBIE_DESTROY_DOOR(1012, Type.SOUND),
/**
* A visual smoke effect. Needs direction as additional info.
*/
SMOKE(2000, Type.VISUAL, BlockFace.class),
/**
* Sound of a block breaking. Needs block ID as additional info.
*/
STEP_SOUND(2001, Type.SOUND, Material.class),
/**
* Visual effect of a splash potion breaking. Needs potion data value as
* additional info.
*/
POTION_BREAK(2002, Type.VISUAL, Potion.class),
/**
* An ender eye signal; a visual effect.
*/
ENDER_SIGNAL(2003, Type.VISUAL),
/**
* The flames seen on a mobspawner; a visual effect.
*/
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 smoke particles that appears on blazes, minecarts
* with furnaces and fire
*/
LARGE_SMOKE("largesmoke", 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("blockcrack", Type.PARTICLE, MaterialData.class),
/**
* The particles generated while sprinting a block
* This particle requires a Material and data value so that the client can select the correct texture.
*/
TILE_DUST("blockdust", Type.PARTICLE, MaterialData.class);
private final int id;
private final Type type;
private final Class<?> data;
private static final Map<Integer, Effect> BY_ID = Maps.newHashMap();
private static final Map<String, Effect> BY_NAME = Maps.newHashMap();
private final String particleName;
private Effect(int id, Type type) {
this(id,type,null);
}
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 if this Effect isn't of type PARTICLE it returns ID of this effect
* @deprecated Magic value
*/
@Deprecated
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.
*/
public Type getType() {
return this.type;
}
/**
* @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;
}
/**
* Gets the Effect associated with the given ID.
*
* @param id ID of the Effect to return
* @return Effect with the given ID
* @deprecated Magic value
*/
@Deprecated
public static Effect getById(int id) {
return BY_ID.get(id);
}
static {
for (Effect effect : values()) {
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, PARTICLE}
}

View File

@ -0,0 +1,136 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* A list of all Effects that can happen to entities.
*/
public enum EntityEffect {
/**
* When mobs get hurt.
*/
HURT(2),
/**
* When a mob dies.
* <p>
* <b>This will cause client-glitches!</b>
*/
DEATH(3),
/**
* The smoke when taming a wolf fails.
* <p>
* Without client-mods this will be ignored if the entity is not a wolf.
*/
WOLF_SMOKE(6),
/**
* The hearts when taming a wolf succeeds.
* <p>
* Without client-mods this will be ignored if the entity is not a wolf.
*/
WOLF_HEARTS(7),
/**
* When a wolf shakes (after being wet).
* <p>
* Without client-mods this will be ignored if the entity is not a wolf.
*/
WOLF_SHAKE(8),
/**
* When a sheep eats a LONG_GRASS block.
*/
SHEEP_EAT(10),
/**
* When an Iron Golem gives a rose.
* <p>
* This will not play an effect if the entity is not an iron golem.
*/
IRON_GOLEM_ROSE(11),
/**
* Hearts from a villager.
* <p>
* This will not play an effect if the entity is not a villager.
*/
VILLAGER_HEART(12),
/**
* When a villager is angry.
* <p>
* This will not play an effect if the entity is not a villager.
*/
VILLAGER_ANGRY(13),
/**
* Happy particles from a villager.
* <p>
* This will not play an effect if the entity is not a villager.
*/
VILLAGER_HAPPY(14),
/**
* Magic particles from a witch.
* <p>
* This will not play an effect if the entity is not a witch.
*/
WITCH_MAGIC(15),
/**
* When a zombie transforms into a villager by shaking violently.
* <p>
* This will not play an effect if the entity is not a zombie.
*/
ZOMBIE_TRANSFORM(16),
/**
* When a firework explodes.
* <p>
* This will not play an effect if the entity is not a firework.
*/
FIREWORK_EXPLODE(17);
private final byte data;
private final static Map<Byte, EntityEffect> BY_DATA = Maps.newHashMap();
EntityEffect(final int data) {
this.data = (byte) data;
}
/**
* Gets the data value of this EntityEffect
*
* @return The data value
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the EntityEffect with the given data value
*
* @param data Data value to fetch
* @return The {@link EntityEffect} representing the given value, or null
* if it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static EntityEffect getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (EntityEffect entityEffect : values()) {
BY_DATA.put(entityEffect.data, entityEffect);
}
}
}

View File

@ -0,0 +1,424 @@
package org.bukkit;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/**
* Represents a single firework effect.
*/
@SerializableAs("Firework")
public final class FireworkEffect implements ConfigurationSerializable {
/**
* The type or shape of the effect.
*/
public enum Type {
/**
* A small ball effect.
*/
BALL,
/**
* A large ball effect.
*/
BALL_LARGE,
/**
* A star-shaped effect.
*/
STAR,
/**
* A burst effect.
*/
BURST,
/**
* A creeper-face effect.
*/
CREEPER,
;
}
/**
* Construct a firework effect.
*
* @return A utility object for building a firework effect
*/
public static Builder builder() {
return new Builder();
}
/**
* This is a builder for FireworkEffects.
*
* @see FireworkEffect#builder()
*/
public static final class Builder {
boolean flicker = false;
boolean trail = false;
final ImmutableList.Builder<Color> colors = ImmutableList.builder();
ImmutableList.Builder<Color> fadeColors = null;
Type type = Type.BALL;
Builder() {}
/**
* Specify the type of the firework effect.
*
* @param type The effect type
* @return This object, for chaining
* @throws IllegalArgumentException If type is null
*/
public Builder with(Type type) throws IllegalArgumentException {
Validate.notNull(type, "Cannot have null type");
this.type = type;
return this;
}
/**
* Add a flicker to the firework effect.
*
* @return This object, for chaining
*/
public Builder withFlicker() {
flicker = true;
return this;
}
/**
* Set whether the firework effect should flicker.
*
* @param flicker true if it should flicker, false if not
* @return This object, for chaining
*/
public Builder flicker(boolean flicker) {
this.flicker = flicker;
return this;
}
/**
* Add a trail to the firework effect.
*
* @return This object, for chaining
*/
public Builder withTrail() {
trail = true;
return this;
}
/**
* Set whether the firework effect should have a trail.
*
* @param trail true if it should have a trail, false for no trail
* @return This object, for chaining
*/
public Builder trail(boolean trail) {
this.trail = trail;
return this;
}
/**
* Add a primary color to the firework effect.
*
* @param color The color to add
* @return This object, for chaining
* @throws IllegalArgumentException If color is null
*/
public Builder withColor(Color color) throws IllegalArgumentException {
Validate.notNull(color, "Cannot have null color");
colors.add(color);
return this;
}
/**
* Add several primary colors to the firework effect.
*
* @param colors The colors to add
* @return This object, for chaining
* @throws IllegalArgumentException If colors is null
* @throws IllegalArgumentException If any color is null (may be
* thrown after changes have occurred)
*/
public Builder withColor(Color...colors) throws IllegalArgumentException {
Validate.notNull(colors, "Cannot have null colors");
if (colors.length == 0) {
return this;
}
ImmutableList.Builder<Color> list = this.colors;
for (Color color : colors) {
Validate.notNull(color, "Color cannot be null");
list.add(color);
}
return this;
}
/**
* Add several primary colors to the firework effect.
*
* @param colors An iterable object whose iterator yields the desired
* colors
* @return This object, for chaining
* @throws IllegalArgumentException If colors is null
* @throws IllegalArgumentException If any color is null (may be
* thrown after changes have occurred)
*/
public Builder withColor(Iterable<?> colors) throws IllegalArgumentException {
Validate.notNull(colors, "Cannot have null colors");
ImmutableList.Builder<Color> list = this.colors;
for (Object color : colors) {
if (!(color instanceof Color)) {
throw new IllegalArgumentException(color + " is not a Color in " + colors);
}
list.add((Color) color);
}
return this;
}
/**
* Add a fade color to the firework effect.
*
* @param color The color to add
* @return This object, for chaining
* @throws IllegalArgumentException If colors is null
* @throws IllegalArgumentException If any color is null (may be
* thrown after changes have occurred)
*/
public Builder withFade(Color color) throws IllegalArgumentException {
Validate.notNull(color, "Cannot have null color");
if (fadeColors == null) {
fadeColors = ImmutableList.builder();
}
fadeColors.add(color);
return this;
}
/**
* Add several fade colors to the firework effect.
*
* @param colors The colors to add
* @return This object, for chaining
* @throws IllegalArgumentException If colors is null
* @throws IllegalArgumentException If any color is null (may be
* thrown after changes have occurred)
*/
public Builder withFade(Color...colors) throws IllegalArgumentException {
Validate.notNull(colors, "Cannot have null colors");
if (colors.length == 0) {
return this;
}
ImmutableList.Builder<Color> list = this.fadeColors;
if (list == null) {
list = this.fadeColors = ImmutableList.builder();
}
for (Color color : colors) {
Validate.notNull(color, "Color cannot be null");
list.add(color);
}
return this;
}
/**
* Add several fade colors to the firework effect.
*
* @param colors An iterable object whose iterator yields the desired
* colors
* @return This object, for chaining
* @throws IllegalArgumentException If colors is null
* @throws IllegalArgumentException If any color is null (may be
* thrown after changes have occurred)
*/
public Builder withFade(Iterable<?> colors) throws IllegalArgumentException {
Validate.notNull(colors, "Cannot have null colors");
ImmutableList.Builder<Color> list = this.fadeColors;
if (list == null) {
list = this.fadeColors = ImmutableList.builder();
}
for (Object color : colors) {
if (!(color instanceof Color)) {
throw new IllegalArgumentException(color + " is not a Color in " + colors);
}
list.add((Color) color);
}
return this;
}
/**
* Create a {@link FireworkEffect} from the current contents of this
* builder.
* <p>
* To successfully build, you must have specified at least one color.
*
* @return The representative firework effect
*/
public FireworkEffect build() {
return new FireworkEffect(
flicker,
trail,
colors.build(),
fadeColors == null ? ImmutableList.<Color>of() : fadeColors.build(),
type
);
}
}
private static final String FLICKER = "flicker";
private static final String TRAIL = "trail";
private static final String COLORS = "colors";
private static final String FADE_COLORS = "fade-colors";
private static final String TYPE = "type";
private final boolean flicker;
private final boolean trail;
private final ImmutableList<Color> colors;
private final ImmutableList<Color> fadeColors;
private final Type type;
private String string = null;
FireworkEffect(boolean flicker, boolean trail, ImmutableList<Color> colors, ImmutableList<Color> fadeColors, Type type) {
if (colors.isEmpty()) {
throw new IllegalStateException("Cannot make FireworkEffect without any color");
}
this.flicker = flicker;
this.trail = trail;
this.colors = colors;
this.fadeColors = fadeColors;
this.type = type;
}
/**
* Get whether the firework effect flickers.
*
* @return true if it flickers, false if not
*/
public boolean hasFlicker() {
return flicker;
}
/**
* Get whether the firework effect has a trail.
*
* @return true if it has a trail, false if not
*/
public boolean hasTrail() {
return trail;
}
/**
* Get the primary colors of the firework effect.
*
* @return An immutable list of the primary colors
*/
public List<Color> getColors() {
return colors;
}
/**
* Get the fade colors of the firework effect.
*
* @return An immutable list of the fade colors
*/
public List<Color> getFadeColors() {
return fadeColors;
}
/**
* Get the type of the firework effect.
*
* @return The effect type
*/
public Type getType() {
return type;
}
/**
* @see ConfigurationSerializable
* @param map the map to deserialize
* @return the resulting serializable
*/
public static ConfigurationSerializable deserialize(Map<String, Object> map) {
Type type = Type.valueOf((String) map.get(TYPE));
if (type == null) {
throw new IllegalArgumentException(map.get(TYPE) + " is not a valid Type");
}
return builder()
.flicker((Boolean) map.get(FLICKER))
.trail((Boolean) map.get(TRAIL))
.withColor((Iterable<?>) map.get(COLORS))
.withFade((Iterable<?>) map.get(FADE_COLORS))
.with(type)
.build();
}
@Override
public Map<String, Object> serialize() {
return ImmutableMap.<String, Object>of(
FLICKER, flicker,
TRAIL, trail,
COLORS, colors,
FADE_COLORS, fadeColors,
TYPE, type.name()
);
}
@Override
public String toString() {
final String string = this.string;
if (string == null) {
return this.string = "FireworkEffect:" + serialize();
}
return string;
}
@Override
public int hashCode() {
/**
* TRUE and FALSE as per boolean.hashCode()
*/
final int PRIME = 31, TRUE = 1231, FALSE = 1237;
int hash = 1;
hash = hash * PRIME + (flicker ? TRUE : FALSE);
hash = hash * PRIME + (trail ? TRUE : FALSE);
hash = hash * PRIME + type.hashCode();
hash = hash * PRIME + colors.hashCode();
hash = hash * PRIME + fadeColors.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof FireworkEffect)) {
return false;
}
FireworkEffect that = (FireworkEffect) obj;
return this.flicker == that.flicker
&& this.trail == that.trail
&& this.type == that.type
&& this.colors.equals(that.colors)
&& this.fadeColors.equals(that.fadeColors);
}
}

View File

@ -0,0 +1,73 @@
package org.bukkit;
import java.util.Map;
import org.bukkit.entity.HumanEntity;
import com.google.common.collect.Maps;
/**
* Represents the various type of game modes that {@link HumanEntity}s may
* have
*/
public enum GameMode {
/**
* Creative mode may fly, build instantly, become invulnerable and create
* free items.
*/
CREATIVE(1),
/**
* Survival mode is the "normal" gameplay type, with no special features.
*/
SURVIVAL(0),
/**
* Adventure mode cannot break blocks without the correct tools.
*/
ADVENTURE(2),
/**
* Spectator mode cannot interact with the world in anyway and is
* invisible to normal players. This grants the player the
* ability to no-clip through the world.
*/
SPECTATOR(3);
private final int value;
private final static Map<Integer, GameMode> BY_ID = Maps.newHashMap();
private GameMode(final int value) {
this.value = value;
}
/**
* Gets the mode value associated with this GameMode
*
* @return An integer value of this gamemode
* @deprecated Magic value
*/
@Deprecated
public int getValue() {
return value;
}
/**
* Gets the GameMode represented by the specified value
*
* @param value Value to check
* @return Associative {@link GameMode} with the given value, or null if
* it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static GameMode getByValue(final int value) {
return BY_ID.get(value);
}
static {
for (GameMode mode : values()) {
BY_ID.put(mode.getValue(), mode);
}
}
}

View File

@ -0,0 +1,61 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the different types of grass.
*/
public enum GrassSpecies {
/**
* Represents the dead looking grass.
*/
DEAD(0x0),
/**
* Represents the normal grass species.
*/
NORMAL(0x1),
/**
* Represents the fern-looking grass species.
*/
FERN_LIKE(0x2);
private final byte data;
private final static Map<Byte, GrassSpecies> BY_DATA = Maps.newHashMap();
private GrassSpecies(final int data) {
this.data = (byte) data;
}
/**
* Gets the associated data value representing this species
*
* @return A byte containing the data value of this grass species
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the GrassSpecies with the given data value
*
* @param data Data value to fetch
* @return The {@link GrassSpecies} representing the given value, or null
* if it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static GrassSpecies getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (GrassSpecies grassSpecies : values()) {
BY_DATA.put(grassSpecies.getData(), grassSpecies);
}
}
}

View File

@ -0,0 +1,67 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
public enum Instrument {
/**
* Piano is the standard instrument for a note block.
*/
PIANO(0x0),
/**
* Bass drum is normally played when a note block is on top of a
* stone-like block
*/
BASS_DRUM(0x1),
/**
* Snare drum is normally played when a note block is on top of a sandy
* block.
*/
SNARE_DRUM(0x2),
/**
* Sticks are normally played when a note block is on top of a glass
* block.
*/
STICKS(0x3),
/**
* Bass guitar is normally played when a note block is on top of a wooden
* block.
*/
BASS_GUITAR(0x4);
private final byte type;
private final static Map<Byte, Instrument> BY_DATA = Maps.newHashMap();
private Instrument(final int type) {
this.type = (byte) type;
}
/**
* @return The type ID of this instrument.
* @deprecated Magic value
*/
@Deprecated
public byte getType() {
return this.type;
}
/**
* Get an instrument by its type ID.
*
* @param type The type ID
* @return The instrument
* @deprecated Magic value
*/
@Deprecated
public static Instrument getByType(final byte type) {
return BY_DATA.get(type);
}
static {
for (Instrument instrument : Instrument.values()) {
BY_DATA.put(instrument.getType(), instrument);
}
}
}

View File

@ -0,0 +1,600 @@
package org.bukkit;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.block.Block;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.util.NumberConversions;
import static org.bukkit.util.NumberConversions.checkFinite;
import org.bukkit.util.Vector;
/**
* Represents a 3-dimensional position in a world
*/
public class Location implements Cloneable, ConfigurationSerializable {
private World world;
private double x;
private double y;
private double z;
private float pitch;
private float yaw;
/**
* Constructs a new Location with the given coordinates
*
* @param world The world in which this location resides
* @param x The x-coordinate of this new location
* @param y The y-coordinate of this new location
* @param z The z-coordinate of this new location
*/
public Location(final World world, final double x, final double y, final double z) {
this(world, x, y, z, 0, 0);
}
/**
* Constructs a new Location with the given coordinates and direction
*
* @param world The world in which this location resides
* @param x The x-coordinate of this new location
* @param y The y-coordinate of this new location
* @param z The z-coordinate of this new location
* @param yaw The absolute rotation on the x-plane, in degrees
* @param pitch The absolute rotation on the y-plane, in degrees
*/
public Location(final World world, final double x, final double y, final double z, final float yaw, final float pitch) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
this.pitch = pitch;
this.yaw = yaw;
}
/**
* Sets the world that this location resides in
*
* @param world New world that this location resides in
*/
public void setWorld(World world) {
this.world = world;
}
/**
* Gets the world that this location resides in
*
* @return World that contains this location
*/
public World getWorld() {
return world;
}
/**
* Gets the chunk at the represented location
*
* @return Chunk at the represented location
*/
public Chunk getChunk() {
return world.getChunkAt(this);
}
/**
* Gets the block at the represented location
*
* @return Block at the represented location
*/
public Block getBlock() {
return world.getBlockAt(this);
}
/**
* Sets the x-coordinate of this location
*
* @param x X-coordinate
*/
public void setX(double x) {
this.x = x;
}
/**
* Gets the x-coordinate of this location
*
* @return x-coordinate
*/
public double getX() {
return x;
}
/**
* Gets the floored value of the X component, indicating the block that
* this location is contained with.
*
* @return block X
*/
public int getBlockX() {
return locToBlock(x);
}
/**
* Sets the y-coordinate of this location
*
* @param y y-coordinate
*/
public void setY(double y) {
this.y = y;
}
/**
* Gets the y-coordinate of this location
*
* @return y-coordinate
*/
public double getY() {
return y;
}
/**
* Gets the floored value of the Y component, indicating the block that
* this location is contained with.
*
* @return block y
*/
public int getBlockY() {
return locToBlock(y);
}
/**
* Sets the z-coordinate of this location
*
* @param z z-coordinate
*/
public void setZ(double z) {
this.z = z;
}
/**
* Gets the z-coordinate of this location
*
* @return z-coordinate
*/
public double getZ() {
return z;
}
/**
* Gets the floored value of the Z component, indicating the block that
* this location is contained with.
*
* @return block z
*/
public int getBlockZ() {
return locToBlock(z);
}
/**
* Sets the yaw of this location, measured in degrees.
* <ul>
* <li>A yaw of 0 or 360 represents the positive z direction.
* <li>A yaw of 180 represents the negative z direction.
* <li>A yaw of 90 represents the negative x direction.
* <li>A yaw of 270 represents the positive x direction.
* </ul>
* Increasing yaw values are the equivalent of turning to your
* right-facing, increasing the scale of the next respective axis, and
* decreasing the scale of the previous axis.
*
* @param yaw new rotation's yaw
*/
public void setYaw(float yaw) {
this.yaw = yaw;
}
/**
* Gets the yaw of this location, measured in degrees.
* <ul>
* <li>A yaw of 0 or 360 represents the positive z direction.
* <li>A yaw of 180 represents the negative z direction.
* <li>A yaw of 90 represents the negative x direction.
* <li>A yaw of 270 represents the positive x direction.
* </ul>
* Increasing yaw values are the equivalent of turning to your
* right-facing, increasing the scale of the next respective axis, and
* decreasing the scale of the previous axis.
*
* @return the rotation's yaw
*/
public float getYaw() {
return yaw;
}
/**
* Sets the pitch of this location, measured in degrees.
* <ul>
* <li>A pitch of 0 represents level forward facing.
* <li>A pitch of 90 represents downward facing, or negative y
* direction.
* <li>A pitch of -90 represents upward facing, or positive y direction.
* </ul>
* Increasing pitch values the equivalent of looking down.
*
* @param pitch new incline's pitch
*/
public void setPitch(float pitch) {
this.pitch = pitch;
}
/**
* Gets the pitch of this location, measured in degrees.
* <ul>
* <li>A pitch of 0 represents level forward facing.
* <li>A pitch of 90 represents downward facing, or negative y
* direction.
* <li>A pitch of -90 represents upward facing, or positive y direction.
* </ul>
* Increasing pitch values the equivalent of looking down.
*
* @return the incline's pitch
*/
public float getPitch() {
return pitch;
}
/**
* Gets a unit-vector pointing in the direction that this Location is
* facing.
*
* @return a vector pointing the direction of this location's {@link
* #getPitch() pitch} and {@link #getYaw() yaw}
*/
public Vector getDirection() {
Vector vector = new Vector();
double rotX = this.getYaw();
double rotY = this.getPitch();
vector.setY(-Math.sin(Math.toRadians(rotY)));
double xz = Math.cos(Math.toRadians(rotY));
vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
return vector;
}
/**
* Sets the {@link #getYaw() yaw} and {@link #getPitch() pitch} to point
* in the direction of the vector.
*
* @param vector the direction vector
* @return the same location
*/
public Location setDirection(Vector vector) {
/*
* Sin = Opp / Hyp
* Cos = Adj / Hyp
* Tan = Opp / Adj
*
* x = -Opp
* z = Adj
*/
final double _2PI = 2 * Math.PI;
final double x = vector.getX();
final double z = vector.getZ();
if (x == 0 && z == 0) {
pitch = vector.getY() > 0 ? -90 : 90;
return this;
}
double theta = Math.atan2(-x, z);
yaw = (float) Math.toDegrees((theta + _2PI) % _2PI);
double x2 = NumberConversions.square(x);
double z2 = NumberConversions.square(z);
double xz = Math.sqrt(x2 + z2);
pitch = (float) Math.toDegrees(Math.atan(-vector.getY() / xz));
return this;
}
/**
* Adds the location by another.
*
* @see Vector
* @param vec The other location
* @return the same location
* @throws IllegalArgumentException for differing worlds
*/
public Location add(Location vec) {
if (vec == null || vec.getWorld() != getWorld()) {
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
}
x += vec.x;
y += vec.y;
z += vec.z;
return this;
}
/**
* Adds the location by a vector.
*
* @see Vector
* @param vec Vector to use
* @return the same location
*/
public Location add(Vector vec) {
this.x += vec.getX();
this.y += vec.getY();
this.z += vec.getZ();
return this;
}
/**
* Adds the location by another. Not world-aware.
*
* @see Vector
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @return the same location
*/
public Location add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
/**
* Subtracts the location by another.
*
* @see Vector
* @param vec The other location
* @return the same location
* @throws IllegalArgumentException for differing worlds
*/
public Location subtract(Location vec) {
if (vec == null || vec.getWorld() != getWorld()) {
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
}
x -= vec.x;
y -= vec.y;
z -= vec.z;
return this;
}
/**
* Subtracts the location by a vector.
*
* @see Vector
* @param vec The vector to use
* @return the same location
*/
public Location subtract(Vector vec) {
this.x -= vec.getX();
this.y -= vec.getY();
this.z -= vec.getZ();
return this;
}
/**
* Subtracts the location by another. Not world-aware and
* orientation independent.
*
* @see Vector
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
* @return the same location
*/
public Location subtract(double x, double y, double z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
}
/**
* Gets the magnitude of the location, defined as sqrt(x^2+y^2+z^2). The
* value of this method is not cached and uses a costly square-root
* function, so do not repeatedly call this method to get the location's
* magnitude. NaN will be returned if the inner result of the sqrt()
* function overflows, which will be caused if the length is too long. Not
* world-aware and orientation independent.
*
* @see Vector
* @return the magnitude
*/
public double length() {
return Math.sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z));
}
/**
* Gets the magnitude of the location squared. Not world-aware and
* orientation independent.
*
* @see Vector
* @return the magnitude
*/
public double lengthSquared() {
return NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z);
}
/**
* Get the distance between this location and another. The value of this
* method is not cached and uses a costly square-root function, so do not
* repeatedly call this method to get the location's magnitude. NaN will
* be returned if the inner result of the sqrt() function overflows, which
* will be caused if the distance is too long.
*
* @see Vector
* @param o The other location
* @return the distance
* @throws IllegalArgumentException for differing worlds
*/
public double distance(Location o) {
return Math.sqrt(distanceSquared(o));
}
/**
* Get the squared distance between this location and another.
*
* @see Vector
* @param o The other location
* @return the distance
* @throws IllegalArgumentException for differing worlds
*/
public double distanceSquared(Location o) {
if (o == null) {
throw new IllegalArgumentException("Cannot measure distance to a null location");
} else if (o.getWorld() == null || getWorld() == null) {
throw new IllegalArgumentException("Cannot measure distance to a null world");
} else if (o.getWorld() != getWorld()) {
throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getName() + " and " + o.getWorld().getName());
}
return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z);
}
/**
* Performs scalar multiplication, multiplying all components with a
* scalar. Not world-aware.
*
* @param m The factor
* @see Vector
* @return the same location
*/
public Location multiply(double m) {
x *= m;
y *= m;
z *= m;
return this;
}
/**
* Zero this location's components. Not world-aware.
*
* @see Vector
* @return the same location
*/
public Location zero() {
x = 0;
y = 0;
z = 0;
return this;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Location other = (Location) obj;
if (this.world != other.world && (this.world == null || !this.world.equals(other.world))) {
return false;
}
if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
return false;
}
if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
return false;
}
if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) {
return false;
}
if (Float.floatToIntBits(this.pitch) != Float.floatToIntBits(other.pitch)) {
return false;
}
if (Float.floatToIntBits(this.yaw) != Float.floatToIntBits(other.yaw)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 19 * hash + (this.world != null ? this.world.hashCode() : 0);
hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
hash = 19 * hash + Float.floatToIntBits(this.pitch);
hash = 19 * hash + Float.floatToIntBits(this.yaw);
return hash;
}
@Override
public String toString() {
return "Location{" + "world=" + world + ",x=" + x + ",y=" + y + ",z=" + z + ",pitch=" + pitch + ",yaw=" + yaw + '}';
}
/**
* Constructs a new {@link Vector} based on this Location
*
* @return New Vector containing the coordinates represented by this
* Location
*/
public Vector toVector() {
return new Vector(x, y, z);
}
@Override
public Location clone() {
try {
return (Location) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
/**
* Safely converts a double (location coordinate) to an int (block
* coordinate)
*
* @param loc Precise coordinate
* @return Block coordinate
*/
public static int locToBlock(double loc) {
return NumberConversions.floor(loc);
}
@Utility
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<String, Object>();
data.put("world", this.world.getName());
data.put("x", this.x);
data.put("y", this.y);
data.put("z", this.z);
data.put("yaw", this.yaw);
data.put("pitch", this.pitch);
return data;
}
/**
* Required method for deserialization
*
* @param args map to deserialize
* @return deserialized location
* @throws IllegalArgumentException if the world don't exists
* @see ConfigurationSerializable
*/
public static Location deserialize(Map<String, Object> args) {
World world = Bukkit.getWorld((String) args.get("world"));
if (world == null) {
throw new IllegalArgumentException("unknown world");
}
return new Location(world, NumberConversions.toDouble(args.get("x")), NumberConversions.toDouble(args.get("y")), NumberConversions.toDouble(args.get("z")), NumberConversions.toFloat(args.get("yaw")), NumberConversions.toFloat(args.get("pitch")));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
package org.bukkit;
public enum NetherWartsState {
/**
* State when first seeded
*/
SEEDED,
/**
* First growth stage
*/
STAGE_ONE,
/**
* Second growth stage
*/
STAGE_TWO,
/**
* Ready to harvest
*/
RIPE;
}

View File

@ -0,0 +1,276 @@
package org.bukkit;
import java.util.Map;
import org.apache.commons.lang.Validate;
import com.google.common.collect.Maps;
/**
* A note class to store a specific note.
*/
public class Note {
/**
* An enum holding tones.
*/
public enum Tone {
G(0x1, true),
A(0x3, true),
B(0x5, false),
C(0x6, true),
D(0x8, true),
E(0xA, false),
F(0xB, true);
private final boolean sharpable;
private final byte id;
private static final Map<Byte, Note.Tone> BY_DATA = Maps.newHashMap();
/** The number of tones including sharped tones. */
public static final byte TONES_COUNT = 12;
private Tone(int id, boolean sharpable) {
this.id = (byte) (id % TONES_COUNT);
this.sharpable = sharpable;
}
/**
* Returns the not sharped id of this tone.
*
* @return the not sharped id of this tone.
* @deprecated Magic value
*/
@Deprecated
public byte getId() {
return getId(false);
}
/**
* Returns the id of this tone. These method allows to return the
* sharped id of the tone. If the tone couldn't be sharped it always
* return the not sharped id of this tone.
*
* @param sharped Set to true to return the sharped id.
* @return the id of this tone.
* @deprecated Magic value
*/
@Deprecated
public byte getId(boolean sharped) {
byte id = (byte) (sharped && sharpable ? this.id + 1 : this.id);
return (byte) (id % TONES_COUNT);
}
/**
* Returns if this tone could be sharped.
*
* @return if this tone could be sharped.
*/
public boolean isSharpable() {
return sharpable;
}
/**
* Returns if this tone id is the sharped id of the tone.
*
* @param id the id of the tone.
* @return if the tone id is the sharped id of the tone.
* @throws IllegalArgumentException if neither the tone nor the
* semitone have the id.
* @deprecated Magic value
*/
@Deprecated
public boolean isSharped(byte id) {
if (id == getId(false)) {
return false;
} else if (id == getId(true)) {
return true;
} else {
// The id isn't matching to the tone!
throw new IllegalArgumentException("The id isn't matching to the tone.");
}
}
/**
* Returns the tone to id. Also returning the semitones.
*
* @param id the id of the tone.
* @return the tone to id.
* @deprecated Magic value
*/
@Deprecated
public static Tone getById(byte id) {
return BY_DATA.get(id);
}
static {
for (Tone tone : values()) {
int id = tone.id % TONES_COUNT;
BY_DATA.put((byte) id, tone);
if (tone.isSharpable()) {
id = (id + 1) % TONES_COUNT;
BY_DATA.put((byte) id, tone);
}
}
}
}
private final byte note;
/**
* Creates a new note.
*
* @param note Internal note id. {@link #getId()} always return this
* value. The value has to be in the interval [0;&nbsp;24].
*/
public Note(int note) {
Validate.isTrue(note >= 0 && note <= 24, "The note value has to be between 0 and 24.");
this.note = (byte) note;
}
/**
* Creates a new note.
*
* @param octave The octave where the note is in. Has to be 0 - 2.
* @param tone The tone within the octave. If the octave is 2 the note has
* to be F#.
* @param sharped Set if the tone is sharped (e.g. for F#).
*/
public Note(int octave, Tone tone, boolean sharped) {
if (sharped && !tone.isSharpable()) {
tone = Tone.values()[tone.ordinal() + 1];
sharped = false;
}
if (octave < 0 || octave > 2 || (octave == 2 && !(tone == Tone.F && sharped))) {
throw new IllegalArgumentException("Tone and octave have to be between F#0 and F#2");
}
this.note = (byte) (octave * Tone.TONES_COUNT + tone.getId(sharped));
}
/**
* Creates a new note for a flat tone, such as A-flat.
*
* @param octave The octave where the note is in. Has to be 0 - 1.
* @param tone The tone within the octave.
* @return The new note.
*/
public static Note flat(int octave, Tone tone) {
Validate.isTrue(octave != 2, "Octave cannot be 2 for flats");
tone = tone == Tone.G ? Tone.F : Tone.values()[tone.ordinal() - 1];
return new Note(octave, tone, tone.isSharpable());
}
/**
* Creates a new note for a sharp tone, such as A-sharp.
*
* @param octave The octave where the note is in. Has to be 0 - 2.
* @param tone The tone within the octave. If the octave is 2 the note has
* to be F#.
* @return The new note.
*/
public static Note sharp(int octave, Tone tone) {
return new Note(octave, tone, true);
}
/**
* Creates a new note for a natural tone, such as A-natural.
*
* @param octave The octave where the note is in. Has to be 0 - 1.
* @param tone The tone within the octave.
* @return The new note.
*/
public static Note natural(int octave, Tone tone) {
Validate.isTrue(octave != 2, "Octave cannot be 2 for naturals");
return new Note(octave, tone, false);
}
/**
* @return The note a semitone above this one.
*/
public Note sharped() {
Validate.isTrue(note < 24, "This note cannot be sharped because it is the highest known note!");
return new Note(note + 1);
}
/**
* @return The note a semitone below this one.
*/
public Note flattened() {
Validate.isTrue(note > 0, "This note cannot be flattened because it is the lowest known note!");
return new Note(note - 1);
}
/**
* Returns the internal id of this note.
*
* @return the internal id of this note.
* @deprecated Magic value
*/
@Deprecated
public byte getId() {
return note;
}
/**
* Returns the octave of this note.
*
* @return the octave of this note.
*/
public int getOctave() {
return note / Tone.TONES_COUNT;
}
private byte getToneByte() {
return (byte) (note % Tone.TONES_COUNT);
}
/**
* Returns the tone of this note.
*
* @return the tone of this note.
*/
public Tone getTone() {
return Tone.getById(getToneByte());
}
/**
* Returns if this note is sharped.
*
* @return if this note is sharped.
*/
public boolean isSharped() {
byte note = getToneByte();
return Tone.getById(note).isSharped(note);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + note;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Note other = (Note) obj;
if (note != other.note)
return false;
return true;
}
@Override
public String toString() {
return "Note{" + getTone().toString() + (isSharped() ? "#" : "") + "}";
}
}

View File

@ -0,0 +1,118 @@
package org.bukkit;
import java.util.Date;
import java.util.UUID;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.AnimalTamer;
import org.bukkit.entity.Player;
import org.bukkit.permissions.ServerOperator;
public interface OfflinePlayer extends ServerOperator, AnimalTamer, ConfigurationSerializable {
/**
* Checks if this player is currently online
*
* @return true if they are online
*/
public boolean isOnline();
/**
* Returns the name of this player
* <p>
* Names are no longer unique past a single game session. For persistent storage
* it is recommended that you use {@link #getUniqueId()} instead.
*
* @return Player name or null if we have not seen a name for this player yet
*/
public String getName();
/**
* Returns the UUID of this player
*
* @return Player UUID
*/
public UUID getUniqueId();
/**
* Checks if this player is banned or not
*
* @return true if banned, otherwise false
*/
public boolean isBanned();
/**
* Bans or unbans this player
*
* @param banned true if banned
* @deprecated Use {@link org.bukkit.BanList#addBan(String, String, Date,
* String)} or {@link org.bukkit.BanList#pardon(String)} to enhance
* functionality
*/
@Deprecated
public void setBanned(boolean banned);
/**
* Checks if this player is whitelisted or not
*
* @return true if whitelisted
*/
public boolean isWhitelisted();
/**
* Sets if this player is whitelisted or not
*
* @param value true if whitelisted
*/
public void setWhitelisted(boolean value);
/**
* Gets a {@link Player} object that this represents, if there is one
* <p>
* If the player is online, this will return that player. Otherwise,
* it will return null.
*
* @return Online player
*/
public Player getPlayer();
/**
* Gets the first date and time that this player was witnessed on this
* server.
* <p>
* If the player has never played before, this will return 0. Otherwise,
* it will be the amount of milliseconds since midnight, January 1, 1970
* UTC.
*
* @return Date of first log-in for this player, or 0
*/
public long getFirstPlayed();
/**
* Gets the last date and time that this player was witnessed on this
* server.
* <p>
* If the player has never played before, this will return 0. Otherwise,
* it will be the amount of milliseconds since midnight, January 1, 1970
* UTC.
*
* @return Date of last log-in for this player, or 0
*/
public long getLastPlayed();
/**
* Checks if this player has played on this server before.
*
* @return True if the player has played before, otherwise false
*/
public boolean hasPlayedBefore();
/**
* Gets the Location where the player will spawn at their bed, null if
* they have not slept in one or their current bed spawn is invalid.
*
* @return Bed Spawn Location if bed exists, otherwise null.
*/
public Location getBedSpawnLocation();
}

View File

@ -0,0 +1,22 @@
package org.bukkit;
/**
* Represents various types of portals that can be made in a world.
*/
public enum PortalType {
/**
* This is a Nether portal, made of obsidian.
*/
NETHER,
/**
* This is an Ender portal.
*/
ENDER,
/**
* This is a custom Plugin portal.
*/
CUSTOM;
}

View File

@ -0,0 +1,63 @@
package org.bukkit;
/**
* An enum to specify a rotation based orientation, like that on a clock.
* <p>
* It represents how something is viewed, as opposed to cardinal directions.
*/
public enum Rotation {
/**
* No rotation
*/
NONE,
/**
* Rotated clockwise by 45 degrees
*/
CLOCKWISE_45,
/**
* Rotated clockwise by 90 degrees
*/
CLOCKWISE,
/**
* Rotated clockwise by 135 degrees
*/
CLOCKWISE_135,
/**
* Flipped upside-down, a 180 degree rotation
*/
FLIPPED,
/**
* Flipped upside-down + 45 degree rotation
*/
FLIPPED_45,
/**
* Rotated counter-clockwise by 90 degrees
*/
COUNTER_CLOCKWISE,
/**
* Rotated counter-clockwise by 45 degrees
*/
COUNTER_CLOCKWISE_45
;
private static final Rotation [] rotations = values();
/**
* Rotate clockwise by 90 degrees.
*
* @return the relative rotation
*/
public Rotation rotateClockwise() {
return rotations[(this.ordinal() + 1) & 0x7];
}
/**
* Rotate counter-clockwise by 90 degrees.
*
* @return the relative rotation
*/
public Rotation rotateCounterClockwise() {
return rotations[(this.ordinal() - 1) & 0x7];
}
}

View File

@ -0,0 +1,51 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the three different types of Sandstone
*/
public enum SandstoneType {
CRACKED(0x0),
GLYPHED(0x1),
SMOOTH(0x2);
private final byte data;
private final static Map<Byte, SandstoneType> BY_DATA = Maps.newHashMap();
private SandstoneType(final int data) {
this.data = (byte) data;
}
/**
* Gets the associated data value representing this type of sandstone
*
* @return A byte containing the data value of this sandstone type
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the type of sandstone with the given data value
*
* @param data Data value to fetch
* @return The {@link SandstoneType} representing the given value, or null
* if it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static SandstoneType getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (SandstoneType type : values()) {
BY_DATA.put(type.data, type);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
package org.bukkit;
/**
* Represents the different types of skulls.
*/
public enum SkullType {
SKELETON,
WITHER,
ZOMBIE,
PLAYER,
CREEPER;
}

View File

@ -0,0 +1,211 @@
package org.bukkit;
/**
* An Enum of Sounds the server is able to send to players.
* <p>
* WARNING: At any time, sounds may be added/removed from this Enum or even
* MineCraft itself! There is no guarantee the sounds will play. There is no
* guarantee values will not be removed from this Enum. As such, you should
* not depend on the ordinal values of this class.
*/
public enum Sound {
AMBIENCE_CAVE,
AMBIENCE_RAIN,
AMBIENCE_THUNDER,
ANVIL_BREAK,
ANVIL_LAND,
ANVIL_USE,
ARROW_HIT,
BURP,
CHEST_CLOSE,
CHEST_OPEN,
CLICK,
DOOR_CLOSE,
DOOR_OPEN,
DRINK,
EAT,
EXPLODE,
FALL_BIG,
FALL_SMALL,
FIRE,
FIRE_IGNITE,
FIZZ,
FUSE,
GLASS,
HURT_FLESH,
ITEM_BREAK,
ITEM_PICKUP,
LAVA,
LAVA_POP,
LEVEL_UP,
MINECART_BASE,
MINECART_INSIDE,
NOTE_BASS,
NOTE_PIANO,
NOTE_BASS_DRUM,
NOTE_STICKS,
NOTE_BASS_GUITAR,
NOTE_SNARE_DRUM,
NOTE_PLING,
ORB_PICKUP,
PISTON_EXTEND,
PISTON_RETRACT,
PORTAL,
PORTAL_TRAVEL,
PORTAL_TRIGGER,
SHOOT_ARROW,
SPLASH,
SPLASH2,
STEP_GRASS,
STEP_GRAVEL,
STEP_LADDER,
STEP_SAND,
STEP_SNOW,
STEP_STONE,
STEP_WOOD,
STEP_WOOL,
SWIM,
WATER,
WOOD_CLICK,
// Mob sounds
BAT_DEATH,
BAT_HURT,
BAT_IDLE,
BAT_LOOP,
BAT_TAKEOFF,
BLAZE_BREATH,
BLAZE_DEATH,
BLAZE_HIT,
CAT_HISS,
CAT_HIT,
CAT_MEOW,
CAT_PURR,
CAT_PURREOW,
CHICKEN_IDLE,
CHICKEN_HURT,
CHICKEN_EGG_POP,
CHICKEN_WALK,
COW_IDLE,
COW_HURT,
COW_WALK,
CREEPER_HISS,
CREEPER_DEATH,
ENDERDRAGON_DEATH,
ENDERDRAGON_GROWL,
ENDERDRAGON_HIT,
ENDERDRAGON_WINGS,
ENDERMAN_DEATH,
ENDERMAN_HIT,
ENDERMAN_IDLE,
ENDERMAN_TELEPORT,
ENDERMAN_SCREAM,
ENDERMAN_STARE,
GHAST_SCREAM,
GHAST_SCREAM2,
GHAST_CHARGE,
GHAST_DEATH,
GHAST_FIREBALL,
GHAST_MOAN,
IRONGOLEM_DEATH,
IRONGOLEM_HIT,
IRONGOLEM_THROW,
IRONGOLEM_WALK,
MAGMACUBE_WALK,
MAGMACUBE_WALK2,
MAGMACUBE_JUMP,
PIG_IDLE,
PIG_DEATH,
PIG_WALK,
SHEEP_IDLE,
SHEEP_SHEAR,
SHEEP_WALK,
SILVERFISH_HIT,
SILVERFISH_KILL,
SILVERFISH_IDLE,
SILVERFISH_WALK,
SKELETON_IDLE,
SKELETON_DEATH,
SKELETON_HURT,
SKELETON_WALK,
SLIME_ATTACK,
SLIME_WALK,
SLIME_WALK2,
SPIDER_IDLE,
SPIDER_DEATH,
SPIDER_WALK,
WITHER_DEATH,
WITHER_HURT,
WITHER_IDLE,
WITHER_SHOOT,
WITHER_SPAWN,
WOLF_BARK,
WOLF_DEATH,
WOLF_GROWL,
WOLF_HOWL,
WOLF_HURT,
WOLF_PANT,
WOLF_SHAKE,
WOLF_WALK,
WOLF_WHINE,
ZOMBIE_METAL,
ZOMBIE_WOOD,
ZOMBIE_WOODBREAK,
ZOMBIE_IDLE,
ZOMBIE_DEATH,
ZOMBIE_HURT,
ZOMBIE_INFECT,
ZOMBIE_UNFECT,
ZOMBIE_REMEDY,
ZOMBIE_WALK,
ZOMBIE_PIG_IDLE,
ZOMBIE_PIG_ANGRY,
ZOMBIE_PIG_DEATH,
ZOMBIE_PIG_HURT,
// Dig Sounds
DIG_WOOL,
DIG_GRASS,
DIG_GRAVEL,
DIG_SAND,
DIG_SNOW,
DIG_STONE,
DIG_WOOD,
// Fireworks
FIREWORK_BLAST,
FIREWORK_BLAST2,
FIREWORK_LARGE_BLAST,
FIREWORK_LARGE_BLAST2,
FIREWORK_TWINKLE,
FIREWORK_TWINKLE2,
FIREWORK_LAUNCH,
SUCCESSFUL_HIT,
// Horses
HORSE_ANGRY,
HORSE_ARMOR,
HORSE_BREATHE,
HORSE_DEATH,
HORSE_GALLOP,
HORSE_HIT,
HORSE_IDLE,
HORSE_JUMP,
HORSE_LAND,
HORSE_SADDLE,
HORSE_SOFT,
HORSE_WOOD,
DONKEY_ANGRY,
DONKEY_DEATH,
DONKEY_HIT,
DONKEY_IDLE,
HORSE_SKELETON_DEATH,
HORSE_SKELETON_HIT,
HORSE_SKELETON_IDLE,
HORSE_ZOMBIE_DEATH,
HORSE_ZOMBIE_HIT,
HORSE_ZOMBIE_IDLE,
// Villager
VILLAGER_DEATH,
VILLAGER_HAGGLE,
VILLAGER_HIT,
VILLAGER_IDLE,
VILLAGER_NO,
VILLAGER_YES,
}

View File

@ -0,0 +1,133 @@
package org.bukkit;
/**
* Represents a countable statistic, which is tracked by the server.
*/
public enum Statistic {
DAMAGE_DEALT,
DAMAGE_TAKEN,
DEATHS,
MOB_KILLS,
PLAYER_KILLS,
FISH_CAUGHT,
ANIMALS_BRED,
TREASURE_FISHED,
JUNK_FISHED,
LEAVE_GAME,
JUMP,
DROP,
PLAY_ONE_TICK,
WALK_ONE_CM,
SWIM_ONE_CM,
FALL_ONE_CM,
CLIMB_ONE_CM,
FLY_ONE_CM,
DIVE_ONE_CM,
MINECART_ONE_CM,
BOAT_ONE_CM,
PIG_ONE_CM,
HORSE_ONE_CM,
SPRINT_ONE_CM,
CROUCH_ONE_CM,
MINE_BLOCK(Type.BLOCK),
USE_ITEM(Type.ITEM),
BREAK_ITEM(Type.ITEM),
CRAFT_ITEM(Type.ITEM),
KILL_ENTITY(Type.ENTITY),
ENTITY_KILLED_BY(Type.ENTITY),
TIME_SINCE_DEATH,
TALKED_TO_VILLAGER,
TRADED_WITH_VILLAGER,
CAKE_SLICES_EATEN,
CAULDRON_FILLED,
CAULDRON_USED,
ARMOR_CLEANED,
BANNER_CLEANED,
BREWINGSTAND_INTERACTION,
BEACON_INTERACTION,
DROPPER_INSPECTED,
HOPPER_INSPECTED,
DISPENSER_INSPECTED,
NOTEBLOCK_PLAYED,
NOTEBLOCK_TUNED,
FLOWER_POTTED,
TRAPPED_CHEST_TRIGGERED,
ENDERCHEST_OPENED,
ITEM_ENCHANTED,
RECORD_PLAYED,
FURNACE_INTERACTION,
CRAFTING_TABLE_INTERACTION,
CHEST_OPENED;
private final Type type;
private Statistic() {
this(Type.UNTYPED);
}
private Statistic(Type type) {
this.type = type;
}
/**
* Gets the type of this statistic.
*
* @return the type of this statistic
*/
public Type getType() {
return type;
}
/**
* Checks if this is a substatistic.
* <p>
* A substatistic exists en masse for each block, item, or entitytype, depending on
* {@link #getType()}.
* <p>
* This is a redundant method and equivalent to checking
* <code>getType() != Type.UNTYPED</code>
*
* @return true if this is a substatistic
*/
public boolean isSubstatistic() {
return type != Type.UNTYPED;
}
/**
* Checks if this is a substatistic dealing with blocks.
* <p>
* This is a redundant method and equivalent to checking
* <code>getType() == Type.BLOCK</code>
*
* @return true if this deals with blocks
*/
public boolean isBlock() {
return type == Type.BLOCK;
}
/**
* The type of statistic.
*
*/
public enum Type {
/**
* Statistics of this type do not require a qualifier.
*/
UNTYPED,
/**
* Statistics of this type require an Item Material qualifier.
*/
ITEM,
/**
* Statistics of this type require a Block Material qualifier.
*/
BLOCK,
/**
* Statistics of this type require an EntityType qualifier.
*/
ENTITY;
}
}

View File

@ -0,0 +1,94 @@
package org.bukkit;
/**
* The Travel Agent handles the creation and the research of Nether and End
* portals when Entities try to use one.
* <p>
* It is used in {@link org.bukkit.event.entity.EntityPortalEvent} and in
* {@link org.bukkit.event.player.PlayerPortalEvent} to help developers
* reproduce and/or modify Vanilla behaviour.
*/
public interface TravelAgent {
/**
* Set the Block radius to search in for available portals.
*
* @param radius the radius in which to search for a portal from the
* location
* @return this travel agent
*/
public TravelAgent setSearchRadius(int radius);
/**
* Gets the search radius value for finding an available portal.
*
* @return the currently set search radius
*/
public int getSearchRadius();
/**
* Sets the maximum radius from the given location to create a portal.
*
* @param radius the radius in which to create a portal from the location
* @return this travel agent
*/
public TravelAgent setCreationRadius(int radius);
/**
* Gets the maximum radius from the given location to create a portal.
*
* @return the currently set creation radius
*/
public int getCreationRadius();
/**
* Returns whether the TravelAgent will attempt to create a destination
* portal or not.
*
* @return whether the TravelAgent should create a destination portal or
* not
*/
public boolean getCanCreatePortal();
/**
* Sets whether the TravelAgent should attempt to create a destination
* portal or not.
*
* @param create Sets whether the TravelAgent should create a destination
* portal or not
*/
public void setCanCreatePortal(boolean create);
/**
* Attempt to find a portal near the given location, if a portal is not
* found it will attempt to create one.
*
* @param location the location where the search for a portal should begin
* @return the location of a portal which has been found or returns the
* location passed to the method if unsuccessful
* @see #createPortal(Location)
*/
public Location findOrCreate(Location location);
/**
* Attempt to find a portal near the given location.
*
* @param location the desired location of the portal
* @return the location of the nearest portal to the location
*/
public Location findPortal(Location location);
/**
* Attempt to create a portal near the given location.
* <p>
* In the case of a Nether portal teleportation, this will attempt to
* create a Nether portal.
* <p>
* In the case of an Ender portal teleportation, this will (re-)create the
* obsidian platform and clean blocks above it.
*
* @param location the desired location of the portal
* @return true if a portal was successfully created
*/
public boolean createPortal(Location location);
}

View File

@ -0,0 +1,74 @@
package org.bukkit;
import java.util.Map;
import com.google.common.collect.Maps;
/**
* Represents the different species of trees regardless of size.
*/
public enum TreeSpecies {
/**
* Represents the common tree species.
*/
GENERIC(0x0),
/**
* Represents the darker barked/leaved tree species.
*/
REDWOOD(0x1),
/**
* Represents birches.
*/
BIRCH(0x2),
/**
* Represents jungle trees.
*/
JUNGLE(0x3),
/**
* Represents acacia trees.
*/
ACACIA(0x4),
/**
* Represents dark oak trees.
*/
DARK_OAK(0x5),
;
private final byte data;
private final static Map<Byte, TreeSpecies> BY_DATA = Maps.newHashMap();
private TreeSpecies(final int data) {
this.data = (byte) data;
}
/**
* Gets the associated data value representing this species
*
* @return A byte containing the data value of this tree species
* @deprecated Magic value
*/
@Deprecated
public byte getData() {
return data;
}
/**
* Gets the TreeSpecies with the given data value
*
* @param data Data value to fetch
* @return The {@link TreeSpecies} representing the given value, or null
* if it doesn't exist
* @deprecated Magic value
*/
@Deprecated
public static TreeSpecies getByData(final byte data) {
return BY_DATA.get(data);
}
static {
for (TreeSpecies species : values()) {
BY_DATA.put(species.data, species);
}
}
}

View File

@ -0,0 +1,72 @@
package org.bukkit;
/**
* Tree and organic structure types.
*/
public enum TreeType {
/**
* Regular tree, no branches
*/
TREE,
/**
* Regular tree, extra tall with branches
*/
BIG_TREE,
/**
* Redwood tree, shaped like a pine tree
*/
REDWOOD,
/**
* Tall redwood tree with just a few leaves at the top
*/
TALL_REDWOOD,
/**
* Birch tree
*/
BIRCH,
/**
* Standard jungle tree; 4 blocks wide and tall
*/
JUNGLE,
/**
* Smaller jungle tree; 1 block wide
*/
SMALL_JUNGLE,
/**
* Jungle tree with cocoa plants; 1 block wide
*/
COCOA_TREE,
/**
* Small bush that grows in the jungle
*/
JUNGLE_BUSH,
/**
* Big red mushroom; short and fat
*/
RED_MUSHROOM,
/**
* Big brown mushroom; tall and umbrella-like
*/
BROWN_MUSHROOM,
/**
* Swamp tree (regular with vines on the side)
*/
SWAMP,
/**
* Acacia tree.
*/
ACACIA,
/**
* Dark Oak tree.
*/
DARK_OAK,
/**
* Mega redwood tree; 4 blocks wide and tall
*/
MEGA_REDWOOD,
/**
* Tall birch tree
*/
TALL_BIRCH,
}

View File

@ -0,0 +1,33 @@
package org.bukkit;
import java.util.List;
import org.bukkit.inventory.ItemStack;
/**
* This interface provides value conversions that may be specific to a
* runtime, or have arbitrary meaning (read: magic values).
* <p>
* Their existence and behavior is not guaranteed across future versions. They
* may be poorly named, throw exceptions, have misleading parameters, or any
* other bad programming practice.
* <p>
* This interface is unsupported and only for internal use.
*
* @deprecated Unsupported {@literal &} internal use only
*/
@Deprecated
public interface UnsafeValues {
Material getMaterialFromInternalName(String name);
List<String> tabCompleteInternalMaterialName(String token, List<String> completions);
ItemStack modifyItemStack(ItemStack stack, String arguments);
Statistic getStatisticFromInternalName(String name);
Achievement getAchievementFromInternalName(String name);
List<String> tabCompleteInternalStatisticOrAchievementName(String token, List<String> completions);
}

View File

@ -0,0 +1,18 @@
package org.bukkit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation indicates a method (and sometimes constructor) will chain
* its internal operations.
* <p>
* This is solely meant for identifying methods that don't need to be
* overridden / handled manually.
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Utility {
}

View File

@ -0,0 +1,109 @@
package org.bukkit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
/**
* This designates the warning state for a specific item.
* <p>
* When the server settings dictate 'default' warnings, warnings are printed
* if the {@link #value()} is true.
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Warning {
/**
* This represents the states that server verbose for warnings may be.
*/
public enum WarningState {
/**
* Indicates all warnings should be printed for deprecated items.
*/
ON,
/**
* Indicates no warnings should be printed for deprecated items.
*/
OFF,
/**
* Indicates each warning would default to the configured {@link
* Warning} annotation, or always if annotation not found.
*/
DEFAULT;
private static final Map<String, WarningState> values = ImmutableMap.<String,WarningState>builder()
.put("off", OFF)
.put("false", OFF)
.put("f", OFF)
.put("no", OFF)
.put("n", OFF)
.put("on", ON)
.put("true", ON)
.put("t", ON)
.put("yes", ON)
.put("y", ON)
.put("", DEFAULT)
.put("d", DEFAULT)
.put("default", DEFAULT)
.build();
/**
* This method checks the provided warning should be printed for this
* state
*
* @param warning The warning annotation added to a deprecated item
* @return <ul>
* <li>ON is always True
* <li>OFF is always false
* <li>DEFAULT is false if and only if annotation is not null and
* specifies false for {@link Warning#value()}, true otherwise.
* </ul>
*/
public boolean printFor(Warning warning) {
if (this == DEFAULT) {
return warning == null || warning.value();
}
return this == ON;
}
/**
* This method returns the corresponding warning state for the given
* string value.
*
* @param value The string value to check
* @return {@link #DEFAULT} if not found, or the respective
* WarningState
*/
public static WarningState value(final String value) {
if (value == null) {
return DEFAULT;
}
WarningState state = values.get(value.toLowerCase());
if (state == null) {
return DEFAULT;
}
return state;
}
}
/**
* This sets if the deprecation warnings when registering events gets
* printed when the setting is in the default state.
*
* @return false normally, or true to encourage warning printout
*/
boolean value() default false;
/**
* This can provide detailed information on why the event is deprecated.
*
* @return The reason an event is deprecated
*/
String reason() default "";
}

View File

@ -0,0 +1,17 @@
package org.bukkit;
/**
* An enum of all current weather types
*/
public enum WeatherType {
/**
* Raining or snowing depending on biome.
*/
DOWNFALL,
/**
* Clear weather, clouds but no rain.
*/
CLEAR,
;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
package org.bukkit;
public interface WorldBorder {
/**
* Resets the border to default values.
*/
public void reset();
/**
* Gets the current side length of the border.
*
* @return The current side length of the border.
*/
public double getSize();
/**
* Sets the border to a square region with the specified side length in blocks.
*
* @param newSize The new size of the border.
*/
public void setSize(double newSize);
/**
* Sets the border to a square region with the specified side length in blocks.
*
* @param newSize The new side length of the border.
* @param seconds The time in seconds in which the border grows or shrinks from the previous size to that being set.
*/
public void setSize(double newSize, long seconds);
/**
* Gets the current border center.
*
* @return The current border center.
*/
public Location getCenter();
/**
* Sets the new border center.
*
* @param x The new center x-coordinate.
* @param z The new center z-coordinate.
*/
public void setCenter(double x, double z);
/**
* Sets the new border center.
*
* @param location The new location of the border center. (Only x/z used)
*/
public void setCenter(Location location);
/**
* Gets the current border damage buffer.
*
* @return The current border damage buffer.
*/
public double getDamageBuffer();
/**
* Sets the amount of blocks a player may safely be outside the border before taking damage.
*
* @param blocks The amount of blocks. (The default is 5 blocks.)
*/
public void setDamageBuffer(double blocks);
/**
* Gets the current border damage amount.
*
* @return The current border damage amount.
*/
public double getDamageAmount();
/**
* Sets the amount of damage a player takes when outside the border plus the border buffer.
*
* @param damage The amount of damage. (The default is 0.2 damage per second per block.)
*/
public void setDamageAmount(double damage);
/**
* Gets the current border warning time in seconds.
*
* @return The current border warning time in seconds.
*/
public int getWarningTime();
/**
* Sets the warning time that causes the screen to be tinted red when a contracting border will reach the player within the specified time.
*
* @param seconds The amount of time in seconds. (The default is 15 seconds.)
*/
public void setWarningTime(int seconds);
/**
* Gets the current border warning distance.
*
* @return The current border warning distance.
*/
public int getWarningDistance();
/**
* Sets the warning distance that causes the screen to be tinted red when the player is within the specified number of blocks from the border.
*
* @param distance The distance in blocks. (The default is 5 blocks.)
*/
public void setWarningDistance(int distance);
}

View File

@ -0,0 +1,317 @@
package org.bukkit;
import java.util.Random;
import org.bukkit.command.CommandSender;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
/**
* Represents various types of options that may be used to create a world.
*/
public class WorldCreator {
private final String name;
private long seed;
private World.Environment environment = World.Environment.NORMAL;
private ChunkGenerator generator = null;
private WorldType type = WorldType.NORMAL;
private boolean generateStructures = true;
private String generatorSettings = "";
/**
* Creates an empty WorldCreationOptions for the given world name
*
* @param name Name of the world that will be created
*/
public WorldCreator(String name) {
if (name == null) {
throw new IllegalArgumentException("World name cannot be null");
}
this.name = name;
this.seed = (new Random()).nextLong();
}
/**
* Copies the options from the specified world
*
* @param world World to copy options from
* @return This object, for chaining
*/
public WorldCreator copy(World world) {
if (world == null) {
throw new IllegalArgumentException("World cannot be null");
}
seed = world.getSeed();
environment = world.getEnvironment();
generator = world.getGenerator();
return this;
}
/**
* Copies the options from the specified {@link WorldCreator}
*
* @param creator World creator to copy options from
* @return This object, for chaining
*/
public WorldCreator copy(WorldCreator creator) {
if (creator == null) {
throw new IllegalArgumentException("Creator cannot be null");
}
seed = creator.seed();
environment = creator.environment();
generator = creator.generator();
return this;
}
/**
* Gets the name of the world that is to be loaded or created.
*
* @return World name
*/
public String name() {
return name;
}
/**
* Gets the seed that will be used to create this world
*
* @return World seed
*/
public long seed() {
return seed;
}
/**
* Sets the seed that will be used to create this world
*
* @param seed World seed
* @return This object, for chaining
*/
public WorldCreator seed(long seed) {
this.seed = seed;
return this;
}
/**
* Gets the environment that will be used to create or load the world
*
* @return World environment
*/
public World.Environment environment() {
return environment;
}
/**
* Sets the environment that will be used to create or load the world
*
* @param env World environment
* @return This object, for chaining
*/
public WorldCreator environment(World.Environment env) {
this.environment = env;
return this;
}
/**
* Gets the type of the world that will be created or loaded
*
* @return World type
*/
public WorldType type() {
return type;
}
/**
* Sets the type of the world that will be created or loaded
*
* @param type World type
* @return This object, for chaining
*/
public WorldCreator type(WorldType type) {
this.type = type;
return this;
}
/**
* Gets the generator that will be used to create or load the world.
* <p>
* This may be null, in which case the "natural" generator for this
* environment will be used.
*
* @return Chunk generator
*/
public ChunkGenerator generator() {
return generator;
}
/**
* Sets the generator that will be used to create or load the world.
* <p>
* This may be null, in which case the "natural" generator for this
* environment will be used.
*
* @param generator Chunk generator
* @return This object, for chaining
*/
public WorldCreator generator(ChunkGenerator generator) {
this.generator = generator;
return this;
}
/**
* Sets the generator that will be used to create or load the world.
* <p>
* This may be null, in which case the "natural" generator for this
* environment will be used.
* <p>
* If the generator cannot be found for the given name, the natural
* environment generator will be used instead and a warning will be
* printed to the console.
*
* @param generator Name of the generator to use, in "plugin:id" notation
* @return This object, for chaining
*/
public WorldCreator generator(String generator) {
this.generator = getGeneratorForName(name, generator, Bukkit.getConsoleSender());
return this;
}
/**
* Sets the generator that will be used to create or load the world.
* <p>
* This may be null, in which case the "natural" generator for this
* environment will be used.
* <p>
* If the generator cannot be found for the given name, the natural
* environment generator will be used instead and a warning will be
* printed to the specified output
*
* @param generator Name of the generator to use, in "plugin:id" notation
* @param output {@link CommandSender} that will receive any error
* messages
* @return This object, for chaining
*/
public WorldCreator generator(String generator, CommandSender output) {
this.generator = getGeneratorForName(name, generator, output);
return this;
}
/**
* Sets the generator settings of the world that will be created or loaded
*
* @param generatorSettings The settings that should be used by the generator
* @return This object, for chaining
*/
public WorldCreator generatorSettings(String generatorSettings) {
this.generatorSettings = generatorSettings;
return this;
}
/**
* Gets the generator settings of the world that will be created or loaded
*
* @return The settings that should be used by the generator
*/
public String generatorSettings() {
return generatorSettings;
}
/**
* Sets whether or not worlds created or loaded with this creator will
* have structures.
*
* @param generate Whether to generate structures
* @return This object, for chaining
*/
public WorldCreator generateStructures(boolean generate) {
this.generateStructures = generate;
return this;
}
/**
* Gets whether or not structures will be generated in the world.
*
* @return True if structures will be generated
*/
public boolean generateStructures() {
return generateStructures;
}
/**
* Creates a world with the specified options.
* <p>
* If the world already exists, it will be loaded from disk and some
* options may be ignored.
*
* @return Newly created or loaded world
*/
public World createWorld() {
return Bukkit.createWorld(this);
}
/**
* Creates a new {@link WorldCreator} for the given world name
*
* @param name Name of the world to load or create
* @return Resulting WorldCreator
*/
public static WorldCreator name(String name) {
return new WorldCreator(name);
}
/**
* Attempts to get the {@link ChunkGenerator} with the given name.
* <p>
* If the generator is not found, null will be returned and a message will
* be printed to the specified {@link CommandSender} explaining why.
* <p>
* The name must be in the "plugin:id" notation, or optionally just
* "plugin", where "plugin" is the safe-name of a plugin and "id" is an
* optional unique identifier for the generator you wish to request from
* the plugin.
*
* @param world Name of the world this will be used for
* @param name Name of the generator to retrieve
* @param output Where to output if errors are present
* @return Resulting generator, or null
*/
public static ChunkGenerator getGeneratorForName(String world, String name, CommandSender output) {
ChunkGenerator result = null;
if (world == null) {
throw new IllegalArgumentException("World name must be specified");
}
if (output == null) {
output = Bukkit.getConsoleSender();
}
if (name != null) {
String[] split = name.split(":", 2);
String id = (split.length > 1) ? split[1] : null;
Plugin plugin = Bukkit.getPluginManager().getPlugin(split[0]);
if (plugin == null) {
output.sendMessage("Could not set generator for world '" + world + "': Plugin '" + split[0] + "' does not exist");
} else if (!plugin.isEnabled()) {
output.sendMessage("Could not set generator for world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled");
} else {
result = plugin.getDefaultWorldGenerator(world, id);
}
}
return result;
}
}

View File

@ -0,0 +1,48 @@
package org.bukkit;
import com.google.common.collect.Maps;
import java.util.Map;
/**
* Represents various types of worlds that may exist
*/
public enum WorldType {
NORMAL("DEFAULT"),
FLAT("FLAT"),
VERSION_1_1("DEFAULT_1_1"),
LARGE_BIOMES("LARGEBIOMES"),
AMPLIFIED("AMPLIFIED"),
CUSTOMIZED("CUSTOMIZED");
private final static Map<String, WorldType> BY_NAME = Maps.newHashMap();
private final String name;
private WorldType(String name) {
this.name = name;
}
/**
* Gets the name of this WorldType
*
* @return Name of this type
*/
public String getName() {
return name;
}
/**
* Gets a Worldtype by its name
*
* @param name Name of the WorldType to get
* @return Requested WorldType, or null if not found
*/
public static WorldType getByName(String name) {
return BY_NAME.get(name.toUpperCase());
}
static {
for (WorldType type : values()) {
BY_NAME.put(type.name, type);
}
}
}

View File

@ -0,0 +1,77 @@
package org.bukkit.block;
import org.bukkit.DyeColor;
import org.bukkit.block.banner.Pattern;
import java.util.List;
public interface Banner extends BlockState {
/**
* Returns the base color for this banner
*
* @return the base color
*/
DyeColor getBaseColor();
/**
* Sets the base color for this banner
*
* @param color the base color
*/
void setBaseColor(DyeColor color);
/**
* Returns a list of patterns on this banner
*
* @return the patterns
*/
List<Pattern> getPatterns();
/**
* Sets the patterns used on this banner
*
* @param patterns the new list of patterns
*/
void setPatterns(List<Pattern> patterns);
/**
* Adds a new pattern on top of the existing
* patterns
*
* @param pattern the new pattern to add
*/
void addPattern(Pattern pattern);
/**
* Returns the pattern at the specified index
*
* @param i the index
* @return the pattern
*/
Pattern getPattern(int i);
/**
* Removes the pattern at the specified index
*
* @param i the index
* @return the removed pattern
*/
Pattern removePattern(int i);
/**
* Sets the pattern at the specified index
*
* @param i the index
* @param pattern the new pattern
*/
void setPattern(int i, Pattern pattern);
/**
* Returns the number of patterns on this
* banner
*
* @return the number of patterns
*/
int numberOfPatterns();
}

View File

@ -0,0 +1,9 @@
package org.bukkit.block;
import org.bukkit.inventory.InventoryHolder;
/**
* Represents a beacon.
*/
public interface Beacon extends BlockState, InventoryHolder {
}

View File

@ -0,0 +1,68 @@
package org.bukkit.block;
/**
* Holds all accepted Biomes in the default server
*/
public enum Biome {
SWAMPLAND,
FOREST,
TAIGA,
DESERT,
PLAINS,
HELL,
SKY,
OCEAN,
RIVER,
EXTREME_HILLS,
FROZEN_OCEAN,
FROZEN_RIVER,
ICE_PLAINS,
ICE_MOUNTAINS,
MUSHROOM_ISLAND,
MUSHROOM_SHORE,
BEACH,
DESERT_HILLS,
FOREST_HILLS,
TAIGA_HILLS,
SMALL_MOUNTAINS,
JUNGLE,
JUNGLE_HILLS,
JUNGLE_EDGE,
DEEP_OCEAN,
STONE_BEACH,
COLD_BEACH,
BIRCH_FOREST,
BIRCH_FOREST_HILLS,
ROOFED_FOREST,
COLD_TAIGA,
COLD_TAIGA_HILLS,
MEGA_TAIGA,
MEGA_TAIGA_HILLS,
EXTREME_HILLS_PLUS,
SAVANNA,
SAVANNA_PLATEAU,
MESA,
MESA_PLATEAU_FOREST,
MESA_PLATEAU,
SUNFLOWER_PLAINS,
DESERT_MOUNTAINS,
FLOWER_FOREST,
TAIGA_MOUNTAINS,
SWAMPLAND_MOUNTAINS,
ICE_PLAINS_SPIKES,
JUNGLE_MOUNTAINS,
JUNGLE_EDGE_MOUNTAINS,
COLD_TAIGA_MOUNTAINS,
SAVANNA_MOUNTAINS,
SAVANNA_PLATEAU_MOUNTAINS,
MESA_BRYCE,
MESA_PLATEAU_FOREST_MOUNTAINS,
MESA_PLATEAU_MOUNTAINS,
BIRCH_FOREST_MOUNTAINS,
BIRCH_FOREST_HILLS_MOUNTAINS,
ROOFED_FOREST_MOUNTAINS,
MEGA_SPRUCE_TAIGA,
EXTREME_HILLS_MOUNTAINS,
EXTREME_HILLS_PLUS_MOUNTAINS,
MEGA_SPRUCE_TAIGA_HILLS,
}

View File

@ -0,0 +1,393 @@
package org.bukkit.block;
import java.util.Collection;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.Metadatable;
/**
* Represents a block. This is a live object, and only one Block may exist for
* any given location in a world. The state of the block may change
* concurrently to your own handling of it; use block.getState() to get a
* snapshot state of a block which will not be modified.
*/
public interface Block extends Metadatable {
/**
* Gets the metadata for this block
*
* @return block specific metadata
* @deprecated Magic value
*/
@Deprecated
byte getData();
/**
* Gets the block at the given offsets
*
* @param modX X-coordinate offset
* @param modY Y-coordinate offset
* @param modZ Z-coordinate offset
* @return Block at the given offsets
*/
Block getRelative(int modX, int modY, int modZ);
/**
* Gets the block at the given face
* <p>
* This method is equal to getRelative(face, 1)
*
* @param face Face of this block to return
* @return Block at the given face
* @see #getRelative(BlockFace, int)
*/
Block getRelative(BlockFace face);
/**
* Gets the block at the given distance of the given face
* <p>
* For example, the following method places water at 100,102,100; two
* blocks above 100,100,100.
*
* <pre>
* Block block = world.getBlockAt(100, 100, 100);
* Block shower = block.getRelative(BlockFace.UP, 2);
* shower.setType(Material.WATER);
* </pre>
*
* @param face Face of this block to return
* @param distance Distance to get the block at
* @return Block at the given face
*/
Block getRelative(BlockFace face, int distance);
/**
* Gets the type of this block
*
* @return block type
*/
Material getType();
/**
* Gets the type-id of this block
*
* @return block type-id
* @deprecated Magic value
*/
@Deprecated
int getTypeId();
/**
* Gets the light level between 0-15
*
* @return light level
*/
byte getLightLevel();
/**
* Get the amount of light at this block from the sky.
* <p>
* Any light given from other sources (such as blocks like torches) will
* be ignored.
*
* @return Sky light level
*/
byte getLightFromSky();
/**
* Get the amount of light at this block from nearby blocks.
* <p>
* Any light given from other sources (such as the sun) will be ignored.
*
* @return Block light level
*/
byte getLightFromBlocks();
/**
* Gets the world which contains this Block
*
* @return World containing this block
*/
World getWorld();
/**
* Gets the x-coordinate of this block
*
* @return x-coordinate
*/
int getX();
/**
* Gets the y-coordinate of this block
*
* @return y-coordinate
*/
int getY();
/**
* Gets the z-coordinate of this block
*
* @return z-coordinate
*/
int getZ();
/**
* Gets the Location of the block
*
* @return Location of block
*/
Location getLocation();
/**
* Stores the location of the block in the provided Location object.
* <p>
* If the provided Location is null this method does nothing and returns
* null.
*
* @param loc the location to copy into
* @return The Location object provided or null
*/
Location getLocation(Location loc);
/**
* Gets the chunk which contains this block
*
* @return Containing Chunk
*/
Chunk getChunk();
/**
* Sets the metadata for this block
*
* @param data New block specific metadata
* @deprecated Magic value
*/
@Deprecated
void setData(byte data);
/**
* Sets the metadata for this block
*
* @param data New block specific metadata
* @param applyPhysics False to cancel physics from the changed block.
* @deprecated Magic value
*/
@Deprecated
void setData(byte data, boolean applyPhysics);
/**
* Sets the type of this block
*
* @param type Material to change this block to
*/
void setType(Material type);
/**
* Sets the type of this block
*
* @param type Material to change this block to
* @param applyPhysics False to cancel physics on the changed block.
*/
void setType(Material type, boolean applyPhysics);
/**
* Sets the type-id of this block
*
* @param type Type-Id to change this block to
* @return whether the block was changed
* @deprecated Magic value
*/
@Deprecated
boolean setTypeId(int type);
/**
* Sets the type-id of this block
*
* @param type Type-Id to change this block to
* @param applyPhysics False to cancel physics on the changed block.
* @return whether the block was changed
* @deprecated Magic value
*/
@Deprecated
boolean setTypeId(int type, boolean applyPhysics);
/**
* Sets the type-id of this block
*
* @param type Type-Id to change this block to
* @param data The data value to change this block to
* @param applyPhysics False to cancel physics on the changed block
* @return whether the block was changed
* @deprecated Magic value
*/
@Deprecated
boolean setTypeIdAndData(int type, byte data, boolean applyPhysics);
/**
* Gets the face relation of this block compared to the given block.
* <p>
* For example:
* <pre>{@code
* Block current = world.getBlockAt(100, 100, 100);
* Block target = world.getBlockAt(100, 101, 100);
*
* current.getFace(target) == BlockFace.Up;
* }</pre>
* <br>
* If the given block is not connected to this block, null may be returned
*
* @param block Block to compare against this block
* @return BlockFace of this block which has the requested block, or null
*/
BlockFace getFace(Block block);
/**
* Captures the current state of this block. You may then cast that state
* into any accepted type, such as Furnace or Sign.
* <p>
* The returned object will never be updated, and you are not guaranteed
* that (for example) a sign is still a sign after you capture its state.
*
* @return BlockState with the current state of this block.
*/
BlockState getState();
/**
* Returns the biome that this block resides in
*
* @return Biome type containing this block
*/
Biome getBiome();
/**
* Sets the biome that this block resides in
*
* @param bio new Biome type for this block
*/
void setBiome(Biome bio);
/**
* Returns true if the block is being powered by Redstone.
*
* @return True if the block is powered.
*/
boolean isBlockPowered();
/**
* Returns true if the block is being indirectly powered by Redstone.
*
* @return True if the block is indirectly powered.
*/
boolean isBlockIndirectlyPowered();
/**
* Returns true if the block face is being powered by Redstone.
*
* @param face The block face
* @return True if the block face is powered.
*/
boolean isBlockFacePowered(BlockFace face);
/**
* Returns true if the block face is being indirectly powered by Redstone.
*
* @param face The block face
* @return True if the block face is indirectly powered.
*/
boolean isBlockFaceIndirectlyPowered(BlockFace face);
/**
* Returns the redstone power being provided to this block face
*
* @param face the face of the block to query or BlockFace.SELF for the
* block itself
* @return The power level.
*/
int getBlockPower(BlockFace face);
/**
* Returns the redstone power being provided to this block
*
* @return The power level.
*/
int getBlockPower();
/**
* Checks if this block is empty.
* <p>
* A block is considered empty when {@link #getType()} returns {@link
* Material#AIR}.
*
* @return true if this block is empty
*/
boolean isEmpty();
/**
* Checks if this block is liquid.
* <p>
* A block is considered liquid when {@link #getType()} returns {@link
* Material#WATER}, {@link Material#STATIONARY_WATER}, {@link
* Material#LAVA} or {@link Material#STATIONARY_LAVA}.
*
* @return true if this block is liquid
*/
boolean isLiquid();
/**
* Gets the temperature of the biome of this block
*
* @return Temperature of this block
*/
double getTemperature();
/**
* Gets the humidity of the biome of this block
*
* @return Humidity of this block
*/
double getHumidity();
/**
* Returns the reaction of the block when moved by a piston
*
* @return reaction
*/
PistonMoveReaction getPistonMoveReaction();
/**
* Breaks the block and spawns items as if a player had digged it
*
* @return true if the block was destroyed
*/
boolean breakNaturally();
/**
* Breaks the block and spawns items as if a player had digged it with a
* specific tool
*
* @param tool The tool or item in hand used for digging
* @return true if the block was destroyed
*/
boolean breakNaturally(ItemStack tool);
/**
* Returns a list of items which would drop by destroying this block
*
* @return a list of dropped items for this type of block
*/
Collection<ItemStack> getDrops();
/**
* Returns a list of items which would drop by destroying this block with
* a specific tool
*
* @param tool The tool or item in hand used for digging
* @return a list of dropped items for this type of block
*/
Collection<ItemStack> getDrops(ItemStack tool);
}

View File

@ -0,0 +1,132 @@
package org.bukkit.block;
/**
* Represents the face of a block
*/
public enum BlockFace {
NORTH(0, 0, -1),
EAST(1, 0, 0),
SOUTH(0, 0, 1),
WEST(-1, 0, 0),
UP(0, 1, 0),
DOWN(0, -1, 0),
NORTH_EAST(NORTH, EAST),
NORTH_WEST(NORTH, WEST),
SOUTH_EAST(SOUTH, EAST),
SOUTH_WEST(SOUTH, WEST),
WEST_NORTH_WEST(WEST, NORTH_WEST),
NORTH_NORTH_WEST(NORTH, NORTH_WEST),
NORTH_NORTH_EAST(NORTH, NORTH_EAST),
EAST_NORTH_EAST(EAST, NORTH_EAST),
EAST_SOUTH_EAST(EAST, SOUTH_EAST),
SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST),
SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST),
WEST_SOUTH_WEST(WEST, SOUTH_WEST),
SELF(0, 0, 0);
private final int modX;
private final int modY;
private final int modZ;
private BlockFace(final int modX, final int modY, final int modZ) {
this.modX = modX;
this.modY = modY;
this.modZ = modZ;
}
private BlockFace(final BlockFace face1, final BlockFace face2) {
this.modX = face1.getModX() + face2.getModX();
this.modY = face1.getModY() + face2.getModY();
this.modZ = face1.getModZ() + face2.getModZ();
}
/**
* Get the amount of X-coordinates to modify to get the represented block
*
* @return Amount of X-coordinates to modify
*/
public int getModX() {
return modX;
}
/**
* Get the amount of Y-coordinates to modify to get the represented block
*
* @return Amount of Y-coordinates to modify
*/
public int getModY() {
return modY;
}
/**
* Get the amount of Z-coordinates to modify to get the represented block
*
* @return Amount of Z-coordinates to modify
*/
public int getModZ() {
return modZ;
}
public BlockFace getOppositeFace() {
switch (this) {
case NORTH:
return BlockFace.SOUTH;
case SOUTH:
return BlockFace.NORTH;
case EAST:
return BlockFace.WEST;
case WEST:
return BlockFace.EAST;
case UP:
return BlockFace.DOWN;
case DOWN:
return BlockFace.UP;
case NORTH_EAST:
return BlockFace.SOUTH_WEST;
case NORTH_WEST:
return BlockFace.SOUTH_EAST;
case SOUTH_EAST:
return BlockFace.NORTH_WEST;
case SOUTH_WEST:
return BlockFace.NORTH_EAST;
case WEST_NORTH_WEST:
return BlockFace.EAST_SOUTH_EAST;
case NORTH_NORTH_WEST:
return BlockFace.SOUTH_SOUTH_EAST;
case NORTH_NORTH_EAST:
return BlockFace.SOUTH_SOUTH_WEST;
case EAST_NORTH_EAST:
return BlockFace.WEST_SOUTH_WEST;
case EAST_SOUTH_EAST:
return BlockFace.WEST_NORTH_WEST;
case SOUTH_SOUTH_EAST:
return BlockFace.NORTH_NORTH_WEST;
case SOUTH_SOUTH_WEST:
return BlockFace.NORTH_NORTH_EAST;
case WEST_SOUTH_WEST:
return BlockFace.EAST_NORTH_EAST;
case SELF:
return BlockFace.SELF;
}
return BlockFace.SELF;
}
}

View File

@ -0,0 +1,206 @@
package org.bukkit.block;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.Metadatable;
/**
* Represents a captured state of a block, which will not change
* automatically.
* <p>
* Unlike Block, which only one object can exist per coordinate, BlockState
* can exist multiple times for any given Block. Note that another plugin may
* change the state of the block and you will not know, or they may change the
* block to another type entirely, causing your BlockState to become invalid.
*/
public interface BlockState extends Metadatable {
/**
* Gets the block represented by this BlockState
*
* @return Block that this BlockState represents
*/
Block getBlock();
/**
* Gets the metadata for this block
*
* @return block specific metadata
*/
MaterialData getData();
/**
* Gets the type of this block
*
* @return block type
*/
Material getType();
/**
* Gets the type-id of this block
*
* @return block type-id
* @deprecated Magic value
*/
@Deprecated
int getTypeId();
/**
* Gets the light level between 0-15
*
* @return light level
*/
byte getLightLevel();
/**
* Gets the world which contains this Block
*
* @return World containing this block
*/
World getWorld();
/**
* Gets the x-coordinate of this block
*
* @return x-coordinate
*/
int getX();
/**
* Gets the y-coordinate of this block
*
* @return y-coordinate
*/
int getY();
/**
* Gets the z-coordinate of this block
*
* @return z-coordinate
*/
int getZ();
/**
* Gets the location of this block
*
* @return location
*/
Location getLocation();
/**
* Stores the location of this block in the provided Location object.
* <p>
* If the provided Location is null this method does nothing and returns
* null.
*
* @param loc the location to copy into
* @return The Location object provided or null
*/
Location getLocation(Location loc);
/**
* Gets the chunk which contains this block
*
* @return Containing Chunk
*/
Chunk getChunk();
/**
* Sets the metadata for this block
*
* @param data New block specific metadata
*/
void setData(MaterialData data);
/**
* Sets the type of this block
*
* @param type Material to change this block to
*/
void setType(Material type);
/**
* Sets the type-id of this block
*
* @param type Type-Id to change this block to
* @return Whether it worked?
* @deprecated Magic value
*/
@Deprecated
boolean setTypeId(int type);
/**
* Attempts to update the block represented by this state, setting it to
* the new values as defined by this state.
* <p>
* This has the same effect as calling update(false). That is to say,
* this will not modify the state of a block if it is no longer the same
* type as it was when this state was taken. It will return false in this
* eventuality.
*
* @return true if the update was successful, otherwise false
* @see #update(boolean)
*/
boolean update();
/**
* Attempts to update the block represented by this state, setting it to
* the new values as defined by this state.
* <p>
* This has the same effect as calling update(force, true). That is to
* say, this will trigger a physics update to surrounding blocks.
*
* @param force true to forcefully set the state
* @return true if the update was successful, otherwise false
*/
boolean update(boolean force);
/**
* Attempts to update the block represented by this state, setting it to
* the new values as defined by this state.
* <p>
* Unless force is true, this will not modify the state of a block if it
* is no longer the same type as it was when this state was taken. It will
* return false in this eventuality.
* <p>
* If force is true, it will set the type of the block to match the new
* state, set the state data and then return true.
* <p>
* If applyPhysics is true, it will trigger a physics update on
* surrounding blocks which could cause them to update or disappear.
*
* @param force true to forcefully set the state
* @param applyPhysics false to cancel updating physics on surrounding
* blocks
* @return true if the update was successful, otherwise false
*/
boolean update(boolean force, boolean applyPhysics);
/**
* @return The data as a raw byte.
* @deprecated Magic value
*/
@Deprecated
public byte getRawData();
/**
* @param data The new data value for the block.
* @deprecated Magic value
*/
@Deprecated
public void setRawData(byte data);
/**
* Returns whether this state is placed in the world.
*
* Some methods will not work if the blockState isn't
* placed in the world.
*
* @return whether the state is placed in the world
* or 'virtual' (e.g. on an itemstack)
*/
boolean isPlaced();
}

View File

@ -0,0 +1,25 @@
package org.bukkit.block;
import org.bukkit.inventory.BrewerInventory;
/**
* Represents a brewing stand.
*/
public interface BrewingStand extends BlockState, ContainerBlock {
/**
* How much time is left in the brewing cycle
*
* @return Brew Time
*/
int getBrewingTime();
/**
* Set the time left before brewing completes.
*
* @param brewTime Brewing time
*/
void setBrewingTime(int brewTime);
public BrewerInventory getInventory();
}

View File

@ -0,0 +1,17 @@
package org.bukkit.block;
import org.bukkit.inventory.Inventory;
/**
* Represents a chest.
*/
public interface Chest extends BlockState, ContainerBlock {
/**
* Returns the chest's inventory. If this is a double chest, it returns
* just the portion of the inventory linked to this half of the chest.
*
* @return The inventory.
*/
Inventory getBlockInventory();
}

View File

@ -0,0 +1,40 @@
package org.bukkit.block;
public interface CommandBlock extends BlockState {
/**
* Gets the command that this CommandBlock will run when powered.
* This will never return null. If the CommandBlock does not have a
* command, an empty String will be returned instead.
*
* @return Command that this CommandBlock will run when powered.
*/
public String getCommand();
/**
* Sets the command that this CommandBlock will run when powered.
* Setting the command to null is the same as setting it to an empty
* String.
*
* @param command Command that this CommandBlock will run when powered.
*/
public void setCommand(String command);
/**
* Gets the name of this CommandBlock. The name is used with commands
* that this CommandBlock executes. This name will never be null, and
* by default is "@".
*
* @return Name of this CommandBlock.
*/
public String getName();
/**
* Sets the name of this CommandBlock. The name is used with commands
* that this CommandBlock executes. Setting the name to null is the
* same as setting it to "@".
*
* @param name New name for this CommandBlock.
*/
public void setName(String name);
}

View File

@ -0,0 +1,11 @@
package org.bukkit.block;
import org.bukkit.inventory.InventoryHolder;
/**
* Indicates a block type that has inventory.
*
* @deprecated in favour of {@link InventoryHolder}
*/
@Deprecated
public interface ContainerBlock extends InventoryHolder {}

View File

@ -0,0 +1,88 @@
package org.bukkit.block;
import org.bukkit.entity.CreatureType;
import org.bukkit.entity.EntityType;
/**
* Represents a creature spawner.
*/
public interface CreatureSpawner extends BlockState {
/**
* Get the spawner's creature type.
*
* @return The creature type.
* @deprecated In favour of {@link #getSpawnedType()}.
*/
@Deprecated
public CreatureType getCreatureType();
/**
* Get the spawner's creature type.
*
* @return The creature type.
*/
public EntityType getSpawnedType();
/**
* Set the spawner's creature type.
*
* @param creatureType The creature type.
*/
public void setSpawnedType(EntityType creatureType);
/**
* Set the spawner creature type.
*
* @param creatureType The creature type.
* @deprecated In favour of {@link #setSpawnedType(EntityType)}.
*/
@Deprecated
public void setCreatureType(CreatureType creatureType);
/**
* Get the spawner's creature type.
*
* @return The creature type's name.
* @deprecated Use {@link #getCreatureTypeName()}.
*/
@Deprecated
public String getCreatureTypeId();
/**
* Set the spawner mob type.
*
* @param creatureType The creature type's name.
*/
public void setCreatureTypeByName(String creatureType);
/**
* Get the spawner's creature type.
*
* @return The creature type's name.
*/
public String getCreatureTypeName();
/**
* Set the spawner mob type.
*
* @param creatureType The creature type's name.
* @deprecated Use {@link #setCreatureTypeByName(String)}.
*/
@Deprecated
public void setCreatureTypeId(String creatureType);
/**
* Get the spawner's delay.
*
* @return The delay.
*/
public int getDelay();
/**
* Set the spawner's delay.
*
* @param delay The delay.
*/
public void setDelay(int delay);
}

View File

@ -0,0 +1,27 @@
package org.bukkit.block;
import org.bukkit.projectiles.BlockProjectileSource;
/**
* Represents a dispenser.
*/
public interface Dispenser extends BlockState, ContainerBlock {
/**
* Gets the BlockProjectileSource object for this dispenser.
* <p>
* If the block is no longer a dispenser, this will return null.
*
* @return a BlockProjectileSource if valid, otherwise null
*/
public BlockProjectileSource getBlockProjectileSource();
/**
* Attempts to dispense the contents of this block.
* <p>
* If the block is no longer a dispenser, this will return false.
*
* @return true if successful, otherwise false
*/
public boolean dispense();
}

View File

@ -0,0 +1,50 @@
package org.bukkit.block;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
/**
* Represents a double chest.
*/
public class DoubleChest implements InventoryHolder {
private DoubleChestInventory inventory;
public DoubleChest(DoubleChestInventory chest) {
inventory = chest;
}
public Inventory getInventory() {
return inventory;
}
public InventoryHolder getLeftSide() {
return inventory.getLeftSide().getHolder();
}
public InventoryHolder getRightSide() {
return inventory.getRightSide().getHolder();
}
public Location getLocation() {
return new Location(getWorld(), getX(), getY(), getZ());
}
public World getWorld() {
return ((Chest)getLeftSide()).getWorld();
}
public double getX() {
return 0.5 * (((Chest)getLeftSide()).getX() + ((Chest)getRightSide()).getX());
}
public double getY() {
return 0.5 * (((Chest)getLeftSide()).getY() + ((Chest)getRightSide()).getY());
}
public double getZ() {
return 0.5 * (((Chest)getLeftSide()).getZ() + ((Chest)getRightSide()).getZ());
}
}

View File

@ -0,0 +1,25 @@
package org.bukkit.block;
import org.bukkit.inventory.InventoryHolder;
/**
* Represents a dropper.
*/
public interface Dropper extends BlockState, InventoryHolder {
/**
* Tries to drop a randomly selected item from the Dropper's inventory,
* following the normal behavior of a Dropper.
* <p>
* Normal behavior of a Dropper is as follows:
* <p>
* If the block that the Dropper is facing is an InventoryHolder or
* ContainerBlock the randomly selected ItemStack is placed within that
* Inventory in the first slot that's available, starting with 0 and
* counting up. If the inventory is full, nothing happens.
* <p>
* If the block that the Dropper is facing is not an InventoryHolder or
* ContainerBlock, the randomly selected ItemStack is dropped on
* the ground in the form of an {@link org.bukkit.entity.Item Item}.
*/
public void drop();
}

View File

@ -0,0 +1,39 @@
package org.bukkit.block;
import org.bukkit.inventory.FurnaceInventory;
/**
* Represents a furnace.
*/
public interface Furnace extends BlockState, ContainerBlock {
/**
* Get burn time.
*
* @return Burn time
*/
public short getBurnTime();
/**
* Set burn time.
*
* @param burnTime Burn time
*/
public void setBurnTime(short burnTime);
/**
* Get cook time.
*
* @return Cook time
*/
public short getCookTime();
/**
* Set cook time.
*
* @param cookTime Cook time
*/
public void setCookTime(short cookTime);
public FurnaceInventory getInventory();
}

View File

@ -0,0 +1,10 @@
package org.bukkit.block;
import org.bukkit.inventory.InventoryHolder;
/**
* Represents a hopper.
*/
public interface Hopper extends BlockState, InventoryHolder {
}

View File

@ -0,0 +1,36 @@
package org.bukkit.block;
import org.bukkit.Material;
/**
* Represents a Jukebox
*/
public interface Jukebox extends BlockState {
/**
* Get the record currently playing
*
* @return The record Material, or AIR if none is playing
*/
public Material getPlaying();
/**
* Set the record currently playing
*
* @param record The record Material, or null/AIR to stop playing
*/
public void setPlaying(Material record);
/**
* Check if the jukebox is currently playing a record
*
* @return True if there is a record playing
*/
public boolean isPlaying();
/**
* Stop the jukebox playing and eject the current record
*
* @return True if a record was ejected; false if there was none playing
*/
public boolean eject();
}

View File

@ -0,0 +1,72 @@
package org.bukkit.block;
import org.bukkit.Instrument;
import org.bukkit.Note;
/**
* Represents a note.
*/
public interface NoteBlock extends BlockState {
/**
* Gets the note.
*
* @return The note.
*/
public Note getNote();
/**
* Gets the note.
*
* @return The note ID.
* @deprecated Magic value
*/
@Deprecated
public byte getRawNote();
/**
* Set the note.
*
* @param note The note.
*/
public void setNote(Note note);
/**
* Set the note.
*
* @param note The note ID.
* @deprecated Magic value
*/
@Deprecated
public void setRawNote(byte note);
/**
* Attempts to play the note at block
* <p>
* If the block is no longer a note block, this will return false
*
* @return true if successful, otherwise false
*/
public boolean play();
/**
* Plays an arbitrary note with an arbitrary instrument
*
* @param instrument Instrument ID
* @param note Note ID
* @return true if successful, otherwise false
* @deprecated Magic value
*/
@Deprecated
public boolean play(byte instrument, byte note);
/**
* Plays an arbitrary note with an arbitrary instrument
*
* @param instrument The instrument
* @param note The note
* @return true if successful, otherwise false
* @see Instrument Note
*/
public boolean play(Instrument instrument, Note note);
}

View File

@ -0,0 +1,51 @@
package org.bukkit.block;
import java.util.HashMap;
import java.util.Map;
public enum PistonMoveReaction {
/**
* Indicates that the block can be pushed or pulled.
*/
MOVE(0),
/**
* Indicates the block is fragile and will break if pushed on.
*/
BREAK(1),
/**
* Indicates that the block will resist being pushed or pulled.
*/
BLOCK(2);
private int id;
private static Map<Integer, PistonMoveReaction> byId = new HashMap<Integer, PistonMoveReaction>();
static {
for (PistonMoveReaction reaction : PistonMoveReaction.values()) {
byId.put(reaction.id, reaction);
}
}
private PistonMoveReaction(int id) {
this.id = id;
}
/**
* @return The ID of the move reaction
* @deprecated Magic value
*/
@Deprecated
public int getId() {
return this.id;
}
/**
* @param id An ID
* @return The move reaction with that ID
* @deprecated Magic value
*/
@Deprecated
public static PistonMoveReaction getById(int id) {
return byId.get(id);
}
}

View File

@ -0,0 +1,37 @@
package org.bukkit.block;
/**
* Represents either a SignPost or a WallSign
*/
public interface Sign extends BlockState {
/**
* Gets all the lines of text currently on this sign.
*
* @return Array of Strings containing each line of text
*/
public String[] getLines();
/**
* Gets the line of text at the specified index.
* <p>
* For example, getLine(0) will return the first line of text.
*
* @param index Line number to get the text from, starting at 0
* @throws IndexOutOfBoundsException Thrown when the line does not exist
* @return Text on the given line
*/
public String getLine(int index) throws IndexOutOfBoundsException;
/**
* Sets the line of text at the specified index.
* <p>
* For example, setLine(0, "Line One") will set the first line of text to
* "Line One".
*
* @param index Line number to set the text at, starting from 0
* @param line New text to set at the specified index
* @throws IndexOutOfBoundsException If the index is out of the range 0..3
*/
public void setLine(int index, String line) throws IndexOutOfBoundsException;
}

View File

@ -0,0 +1,62 @@
package org.bukkit.block;
import org.bukkit.SkullType;
/**
* Represents a Skull
*/
public interface Skull extends BlockState {
/**
* Checks to see if the skull has an owner
*
* @return true if the skull has an owner
*/
public boolean hasOwner();
/**
* Gets the owner of the skull, if one exists
*
* @return the owner of the skull or null if the skull does not have an owner
*/
public String getOwner();
/**
* Sets the owner of the skull
* <p>
* Involves a potentially blocking web request to acquire the profile data for
* the provided name.
*
* @param name the new owner of the skull
* @return true if the owner was successfully set
*/
public boolean setOwner(String name);
/**
* Gets the rotation of the skull in the world
*
* @return the rotation of the skull
*/
public BlockFace getRotation();
/**
* Sets the rotation of the skull in the world
*
* @param rotation the rotation of the skull
*/
public void setRotation(BlockFace rotation);
/**
* Gets the type of skull
*
* @return the type of skull
*/
public SkullType getSkullType();
/**
* Sets the type of skull
*
* @param skullType the type of skull
*/
public void setSkullType(SkullType skullType);
}

View File

@ -0,0 +1,94 @@
package org.bukkit.block.banner;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.NoSuchElementException;
import org.bukkit.DyeColor;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
@SerializableAs("Pattern")
public class Pattern implements ConfigurationSerializable {
private static final String COLOR = "color";
private static final String PATTERN = "pattern";
private final DyeColor color;
private final PatternType pattern;
/**
* Creates a new pattern from the specified color and
* pattern type
*
* @param color the pattern color
* @param pattern the pattern type
*/
public Pattern(DyeColor color, PatternType pattern) {
this.color = color;
this.pattern = pattern;
}
/**
* Constructor for deserialization.
*
* @param map the map to deserialize from
*/
public Pattern(Map<String, Object> map) {
color = DyeColor.valueOf(getString(map, COLOR));
pattern = PatternType.getByIdentifier(getString(map, PATTERN));
}
private static String getString(Map<?,?> map, Object key) {
Object str = map.get(key);
if (str instanceof String) {
return (String) str;
}
throw new NoSuchElementException(map + " does not contain " + key);
}
@Override
public Map<String, Object> serialize() {
return ImmutableMap.<String, Object>of(
COLOR, color.toString(),
PATTERN, pattern.getIdentifier()
);
}
/**
* Returns the color of the pattern
*
* @return the color of the pattern
*/
public DyeColor getColor() {
return color;
}
/**
* Returns the type of pattern
*
* @return the pattern type
*/
public PatternType getPattern() {
return pattern;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.color != null ? this.color.hashCode() : 0);
hash = 97 * hash + (this.pattern != null ? this.pattern.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pattern other = (Pattern) obj;
return this.color == other.color && this.pattern == other.pattern;
}
}

View File

@ -0,0 +1,80 @@
package org.bukkit.block.banner;
import java.util.HashMap;
import java.util.Map;
public enum PatternType {
BASE("b"),
SQUARE_BOTTOM_LEFT("bl"),
SQUARE_BOTTOM_RIGHT("br"),
SQUARE_TOP_LEFT("tl"),
SQUARE_TOP_RIGHT("tr"),
STRIPE_BOTTOM("bs"),
STRIPE_TOP("ts"),
STRIPE_LEFT("ls"),
STRIPE_RIGHT("rs"),
STRIPE_CENTER("cs"),
STRIPE_MIDDLE("ms"),
STRIPE_DOWNRIGHT("drs"),
STRIPE_DOWNLEFT("dls"),
STRIPE_SMALL("ss"),
CROSS("cr"),
STRAIGHT_CROSS("sc"),
TRIANGLE_BOTTOM("bt"),
TRIANGLE_TOP("tt"),
TRIANGLES_BOTTOM("bts"),
TRIANGLES_TOP("tts"),
DIAGONAL_LEFT("ld"),
DIAGONAL_RIGHT("rd"),
DIAGONAL_LEFT_MIRROR("lud"),
DIAGONAL_RIGHT_MIRROR("rud"),
CIRCLE_MIDDLE("mc"),
RHOMBUS_MIDDLE("mr"),
HALF_VERTICAL("vh"),
HALF_HORIZONTAL("hh"),
HALF_VERTICAL_MIRROR("vhr"),
HALF_HORIZONTAL_MIRROR("hhb"),
BORDER("bo"),
CURLY_BORDER("cbo"),
CREEPER("cre"),
GRADIENT("gra"),
GRADIENT_UP("gru"),
BRICKS("bri"),
SKULL("sku"),
FLOWER("flo"),
MOJANG("moj");
private final String identifier;
private static final Map<String, PatternType> byString = new HashMap<String, PatternType>();
static {
for (PatternType p : values()) {
byString.put(p.identifier, p);
}
}
private PatternType(String key) {
this.identifier = key;
}
/**
* Returns the identifier used to represent
* this pattern type
*
* @return the pattern's identifier
*/
public String getIdentifier() {
return identifier;
}
/**
* Returns the pattern type which matches the passed
* identifier or null if no matches are found
*
* @param identifier the identifier
* @return the matched pattern type or null
*/
public static PatternType getByIdentifier(String identifier) {
return byString.get(identifier);
}
}

View File

@ -0,0 +1,13 @@
package org.bukkit.command;
import org.bukkit.block.Block;
public interface BlockCommandSender extends CommandSender {
/**
* Returns the block this command sender belongs to
*
* @return Block for the command sender
*/
public Block getBlock();
}

View File

@ -0,0 +1,450 @@
package org.bukkit.command;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.CommandMinecart;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.util.StringUtil;
import com.google.common.collect.ImmutableList;
/**
* Represents a Command, which executes various tasks upon user input
*/
public abstract class Command {
private String name;
private String nextLabel;
private String label;
private List<String> aliases;
private List<String> activeAliases;
private CommandMap commandMap = null;
protected String description = "";
protected String usageMessage;
private String permission;
private String permissionMessage;
public co.aikar.timings.Timing timings; // Spigot
public String getTimingName() {return getName();} // Spigot
protected Command(String name) {
this(name, "", "/" + name, new ArrayList<String>());
}
protected Command(String name, String description, String usageMessage, List<String> aliases) {
this.name = name;
this.nextLabel = name;
this.label = name;
this.description = description;
this.usageMessage = usageMessage;
this.aliases = aliases;
this.activeAliases = new ArrayList<String>(aliases);
}
/**
* Executes the command, returning its success
*
* @param sender Source object which is executing this command
* @param commandLabel The alias of the command used
* @param args All arguments passed to the command, split via ' '
* @return true if the command was successful, otherwise false
*/
public abstract boolean execute(CommandSender sender, String commandLabel, String[] args);
/**
* Executed on tab completion for this command, returning a list of
* options the player can tab through.
*
* @deprecated This method is not supported and returns null
* @param sender Source object which is executing this command
* @param args All arguments passed to the command, split via ' '
* @return a list of tab-completions for the specified arguments. This
* will never be null. List may be immutable.
*/
@Deprecated
public List<String> tabComplete(CommandSender sender, String[] args) {
return null;
}
/**
* Executed on tab completion for this command, returning a list of
* options the player can tab through.
*
* @param sender Source object which is executing this command
* @param alias the alias being used
* @param args All arguments passed to the command, split via ' '
* @return a list of tab-completions for the specified arguments. This
* will never be null. List may be immutable.
* @throws IllegalArgumentException if sender, alias, or args is null
*/
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
Validate.notNull(sender, "Sender cannot be null");
Validate.notNull(args, "Arguments cannot be null");
Validate.notNull(alias, "Alias cannot be null");
if (args.length == 0) {
return ImmutableList.of();
}
String lastWord = args[args.length - 1];
Player senderPlayer = sender instanceof Player ? (Player) sender : null;
ArrayList<String> matchedPlayers = new ArrayList<String>();
for (Player player : sender.getServer().getOnlinePlayers()) {
String name = player.getName();
if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) {
matchedPlayers.add(name);
}
}
Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER);
return matchedPlayers;
}
// PaperSpigot start - location tab-completes
/**
* Executed on tab completion for this command, returning a list of options the player can tab through. This method
* returns the {@link Location} of the block the player is looking at at the time of the tab complete.
* <p>
* Commands that want to use the Location information in their tab-complete implementations need to override this
* method. The Location provided by this method is the block that the player is currently looking at when the player
* attempts the tab complete. For this to be valid, the block must be highlighted by the player (i.e. the player is
* close enough to interact with the block).
*
* @param sender Source object which is executing this command
* @param alias the alias being used
* @param args All arguments passed to the command, split via ' '
* @param location the location of the block the player is looking at
* @return a list of tab-completions for the specified arguments. This
* will never be null. List may be immutable.
* @throws IllegalArgumentException if sender, alias, or args is null
*/
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
// Simply default to the standard tab-complete, subclasses can override this if needed
return tabComplete(sender, alias, args);
}
// PaperSpigot end
/**
* Returns the name of this command
*
* @return Name of this command
*/
public String getName() {
return name;
}
/**
* Sets the name of this command.
* <p>
* May only be used before registering the command.
* Will return true if the new name is set, and false
* if the command has already been registered.
*
* @param name New command name
* @return returns true if the name change happened instantly or false if
* the command was already registered
*/
public boolean setName(String name) {
if (!isRegistered()) {
this.name = name;
return true;
}
return false;
}
/**
* Gets the permission required by users to be able to perform this
* command
*
* @return Permission name, or null if none
*/
public String getPermission() {
return permission;
}
/**
* Sets the permission required by users to be able to perform this
* command
*
* @param permission Permission name or null
*/
public void setPermission(String permission) {
this.permission = permission;
}
/**
* Tests the given {@link CommandSender} to see if they can perform this
* command.
* <p>
* If they do not have permission, they will be informed that they cannot
* do this.
*
* @param target User to test
* @return true if they can use it, otherwise false
*/
public boolean testPermission(CommandSender target) {
if (testPermissionSilent(target)) {
return true;
}
if (permissionMessage == null) {
target.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.");
} else if (permissionMessage.length() != 0) {
for (String line : permissionMessage.replace("<permission>", permission).split("\n")) {
target.sendMessage(line);
}
}
return false;
}
/**
* Tests the given {@link CommandSender} to see if they can perform this
* command.
* <p>
* No error is sent to the sender.
*
* @param target User to test
* @return true if they can use it, otherwise false
*/
public boolean testPermissionSilent(CommandSender target) {
if ((permission == null) || (permission.length() == 0)) {
return true;
}
for (String p : permission.split(";")) {
if (target.hasPermission(p)) {
return true;
}
}
return false;
}
/**
* Returns the label for this command
*
* @return Label of this command
*/
public String getLabel() {
return label;
}
/**
* Sets the label of this command.
* <p>
* May only be used before registering the command.
* Will return true if the new name is set, and false
* if the command has already been registered.
*
* @param name The command's name
* @return returns true if the name change happened instantly or false if
* the command was already registered
*/
public boolean setLabel(String name) {
this.nextLabel = name;
if (!isRegistered()) {
this.label = name;
return true;
}
return false;
}
/**
* Registers this command to a CommandMap.
* Once called it only allows changes the registered CommandMap
*
* @param commandMap the CommandMap to register this command to
* @return true if the registration was successful (the current registered
* CommandMap was the passed CommandMap or null) false otherwise
*/
public boolean register(CommandMap commandMap) {
if (allowChangesFrom(commandMap)) {
this.commandMap = commandMap;
return true;
}
return false;
}
/**
* Unregisters this command from the passed CommandMap applying any
* outstanding changes
*
* @param commandMap the CommandMap to unregister
* @return true if the unregistration was successfull (the current
* registered CommandMap was the passed CommandMap or null) false
* otherwise
*/
public boolean unregister(CommandMap commandMap) {
if (allowChangesFrom(commandMap)) {
this.commandMap = null;
this.activeAliases = new ArrayList<String>(this.aliases);
this.label = this.nextLabel;
return true;
}
return false;
}
private boolean allowChangesFrom(CommandMap commandMap) {
return (null == this.commandMap || this.commandMap == commandMap);
}
/**
* Returns the current registered state of this command
*
* @return true if this command is currently registered false otherwise
*/
public boolean isRegistered() {
return (null != this.commandMap);
}
/**
* Returns a list of active aliases of this command
*
* @return List of aliases
*/
public List<String> getAliases() {
return activeAliases;
}
/**
* Returns a message to be displayed on a failed permission check for this
* command
*
* @return Permission check failed message
*/
public String getPermissionMessage() {
return permissionMessage;
}
/**
* Gets a brief description of this command
*
* @return Description of this command
*/
public String getDescription() {
return description;
}
/**
* Gets an example usage of this command
*
* @return One or more example usages
*/
public String getUsage() {
return usageMessage;
}
/**
* Sets the list of aliases to request on registration for this command.
* This is not effective outside of defining aliases in the {@link
* PluginDescriptionFile#getCommands()} (under the
* `<code>aliases</code>' node) is equivalent to this method.
*
* @param aliases aliases to register to this command
* @return this command object, for chaining
*/
public Command setAliases(List<String> aliases) {
this.aliases = aliases;
if (!isRegistered()) {
this.activeAliases = new ArrayList<String>(aliases);
}
return this;
}
/**
* Sets a brief description of this command. Defining a description in the
* {@link PluginDescriptionFile#getCommands()} (under the
* `<code>description</code>' node) is equivalent to this method.
*
* @param description new command description
* @return this command object, for chaining
*/
public Command setDescription(String description) {
this.description = description;
return this;
}
/**
* Sets the message sent when a permission check fails
*
* @param permissionMessage new permission message, null to indicate
* default message, or an empty string to indicate no message
* @return this command object, for chaining
*/
public Command setPermissionMessage(String permissionMessage) {
this.permissionMessage = permissionMessage;
return this;
}
/**
* Sets the example usage of this command
*
* @param usage new example usage
* @return this command object, for chaining
*/
public Command setUsage(String usage) {
this.usageMessage = usage;
return this;
}
public static void broadcastCommandMessage(CommandSender source, String message) {
broadcastCommandMessage(source, message, true);
}
public static void broadcastCommandMessage(CommandSender source, String message, boolean sendToSource) {
String result = source.getName() + ": " + message;
if (source instanceof BlockCommandSender) {
BlockCommandSender blockCommandSender = (BlockCommandSender) source;
if (blockCommandSender.getBlock().getWorld().getGameRuleValue("commandBlockOutput").equalsIgnoreCase("false")) {
Bukkit.getConsoleSender().sendMessage(result);
return;
}
} else if (source instanceof CommandMinecart) {
CommandMinecart commandMinecart = (CommandMinecart) source;
if (commandMinecart.getWorld().getGameRuleValue("commandBlockOutput").equalsIgnoreCase("false")) {
Bukkit.getConsoleSender().sendMessage(result);
return;
}
}
Set<Permissible> users = Bukkit.getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
String colored = ChatColor.GRAY + "" + ChatColor.ITALIC + "[" + result + ChatColor.GRAY + ChatColor.ITALIC + "]";
if (sendToSource && !(source instanceof ConsoleCommandSender)) {
source.sendMessage(message);
}
for (Permissible user : users) {
if (user instanceof CommandSender) {
CommandSender target = (CommandSender) user;
if (target instanceof ConsoleCommandSender) {
target.sendMessage(result);
} else if (target != source) {
target.sendMessage(colored);
}
}
}
}
@Override
public String toString() {
return getClass().getName() + '(' + name + ')';
}
}

View File

@ -0,0 +1,28 @@
package org.bukkit.command;
/**
* Thrown when an unhandled exception occurs during the execution of a Command
*/
@SuppressWarnings("serial")
public class CommandException extends RuntimeException {
/**
* Creates a new instance of <code>CommandException</code> without detail
* message.
*/
public CommandException() {}
/**
* Constructs an instance of <code>CommandException</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public CommandException(String msg) {
super(msg);
}
public CommandException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,18 @@
package org.bukkit.command;
/**
* Represents a class which contains a single method for executing commands
*/
public interface CommandExecutor {
/**
* Executes the given command, returning its success
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
public boolean onCommand(CommandSender sender, Command command, String label, String[] args);
}

View File

@ -0,0 +1,109 @@
package org.bukkit.command;
import java.util.List;
public interface CommandMap {
/**
* Registers all the commands belonging to a certain plugin.
* <p>
* Caller can use:-
* <ul>
* <li>command.getName() to determine the label registered for this
* command
* <li>command.getAliases() to determine the aliases which where
* registered
* </ul>
*
* @param fallbackPrefix a prefix which is prepended to each command with
* a ':' one or more times to make the command unique
* @param commands a list of commands to register
*/
public void registerAll(String fallbackPrefix, List<Command> commands);
/**
* Registers a command. Returns true on success; false if name is already
* taken and fallback had to be used.
* <p>
* Caller can use:-
* <ul>
* <li>command.getName() to determine the label registered for this
* command
* <li>command.getAliases() to determine the aliases which where
* registered
* </ul>
*
* @param label the label of the command, without the '/'-prefix.
* @param fallbackPrefix a prefix which is prepended to the command with a
* ':' one or more times to make the command unique
* @param command the command to register
* @return true if command was registered with the passed in label, false
* otherwise, which indicates the fallbackPrefix was used one or more
* times
*/
public boolean register(String label, String fallbackPrefix, Command command);
/**
* Registers a command. Returns true on success; false if name is already
* taken and fallback had to be used.
* <p>
* Caller can use:-
* <ul>
* <li>command.getName() to determine the label registered for this
* command
* <li>command.getAliases() to determine the aliases which where
* registered
* </ul>
*
* @param fallbackPrefix a prefix which is prepended to the command with a
* ':' one or more times to make the command unique
* @param command the command to register, from which label is determined
* from the command name
* @return true if command was registered with the passed in label, false
* otherwise, which indicates the fallbackPrefix was used one or more
* times
*/
public boolean register(String fallbackPrefix, Command command);
/**
* Looks for the requested command and executes it if found.
*
* @param sender The command's sender
* @param cmdLine command + arguments. Example: "/test abc 123"
* @return returns false if no target is found, true otherwise.
* @throws CommandException Thrown when the executor for the given command
* fails with an unhandled exception
*/
public boolean dispatch(CommandSender sender, String cmdLine) throws CommandException;
/**
* Clears all registered commands.
*/
public void clearCommands();
/**
* Gets the command registered to the specified name
*
* @param name Name of the command to retrieve
* @return Command with the specified name or null if a command with that
* label doesn't exist
*/
public Command getCommand(String name);
/**
* Looks for the requested command and executes an appropriate
* tab-completer if found. This method will also tab-complete partial
* commands.
*
* @param sender The command's sender.
* @param cmdLine The entire command string to tab-complete, excluding
* initial slash.
* @return a list of possible tab-completions. This list may be immutable.
* Will be null if no matching command of which sender has permission.
* @throws CommandException Thrown when the tab-completer for the given
* command fails with an unhandled exception
* @throws IllegalArgumentException if either sender or cmdLine are null
*/
public List<String> tabComplete(CommandSender sender, String cmdLine) throws IllegalArgumentException;
}

View File

@ -0,0 +1,61 @@
package org.bukkit.command;
import org.bukkit.Server;
import org.bukkit.permissions.Permissible;
public interface CommandSender extends Permissible {
/**
* Sends this sender a message
*
* @param message Message to be displayed
*/
public void sendMessage(String message);
/**
* Sends this sender multiple messages
*
* @param messages An array of messages to be displayed
*/
public void sendMessage(String[] messages);
/**
* Returns the server instance that this command is running on
*
* @return Server instance
*/
public Server getServer();
/**
* Gets the name of this command sender
*
* @return Name of the sender
*/
public String getName();
// Paper start
/**
* Sends the component to the sender
*
* <p>If this sender does not support sending full components then
* the component will be sent as legacy text.</p>
*
* @param component the component to send
*/
default void sendMessage(net.md_5.bungee.api.chat.BaseComponent component) {
this.sendMessage(component.toLegacyText());
}
/**
* Sends an array of components as a single message to the sender
*
* <p>If this sender does not support sending full components then
* the components will be sent as legacy text.</p>
*
* @param components the components to send
*/
default void sendMessage(net.md_5.bungee.api.chat.BaseComponent... components) {
this.sendMessage(new net.md_5.bungee.api.chat.TextComponent(components).toLegacyText());
}
// Paper end
}

View File

@ -0,0 +1,6 @@
package org.bukkit.command;
import org.bukkit.conversations.Conversable;
public interface ConsoleCommandSender extends CommandSender, Conversable {
}

View File

@ -0,0 +1,128 @@
package org.bukkit.command;
import java.util.ArrayList;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.server.RemoteServerCommandEvent;
import org.bukkit.event.server.ServerCommandEvent;
public class FormattedCommandAlias extends Command {
private final String[] formatStrings;
public FormattedCommandAlias(String alias, String[] formatStrings) {
super(alias);
timings = co.aikar.timings.TimingsManager.getCommandTiming("minecraft", this); // Spigot
this.formatStrings = formatStrings;
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
boolean result = false;
ArrayList<String> commands = new ArrayList<String>();
for (String formatString : formatStrings) {
try {
commands.add(buildCommand(formatString, args));
} catch (Throwable throwable) {
if (throwable instanceof IllegalArgumentException) {
sender.sendMessage(throwable.getMessage());
} else {
sender.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command");
}
return false;
}
}
for (String command : commands) {
result |= Bukkit.dispatchCommand(sender, command);
}
return result;
}
private String buildCommand(String formatString, String[] args) {
int index = formatString.indexOf("$");
while (index != -1) {
int start = index;
if (index > 0 && formatString.charAt(start - 1) == '\\') {
formatString = formatString.substring(0, start - 1) + formatString.substring(start);
index = formatString.indexOf("$", index);
continue;
}
boolean required = false;
if (formatString.charAt(index + 1) == '$') {
required = true;
// Move index past the second $
index++;
}
// Move index past the $
index++;
int argStart = index;
while (index < formatString.length() && inRange(((int) formatString.charAt(index)) - 48, 0, 9)) {
// Move index past current digit
index++;
}
// No numbers found
if (argStart == index) {
throw new IllegalArgumentException("Invalid replacement token");
}
int position = Integer.valueOf(formatString.substring(argStart, index));
// Arguments are not 0 indexed
if (position == 0) {
throw new IllegalArgumentException("Invalid replacement token");
}
// Convert position to 0 index
position--;
boolean rest = false;
if (index < formatString.length() && formatString.charAt(index) == '-') {
rest = true;
// Move index past the -
index++;
}
int end = index;
if (required && position >= args.length) {
throw new IllegalArgumentException("Missing required argument " + (position + 1));
}
StringBuilder replacement = new StringBuilder();
if (rest && position < args.length) {
for (int i = position; i < args.length; i++) {
if (i != position) {
replacement.append(' ');
}
replacement.append(args[i]);
}
} else if (position < args.length) {
replacement.append(args[position]);
}
formatString = formatString.substring(0, start) + replacement.toString() + formatString.substring(end);
// Move index past the replaced data so we don't process it again
index = start + replacement.length();
// Move to the next replacement token
index = formatString.indexOf("$", index);
}
return formatString;
}
@Override // Spigot
public String getTimingName() {return "Command Forwarder - " + super.getTimingName();} // Spigot
private static boolean inRange(int i, int j, int k) {
return i >= j && i <= k;
}
}

View File

@ -0,0 +1,33 @@
package org.bukkit.command;
/**
* Represents a command that delegates to one or more other commands
*/
public class MultipleCommandAlias extends Command {
private Command[] commands;
public MultipleCommandAlias(String name, Command[] commands) {
super(name);
this.commands = commands;
}
/**
* Gets the commands associated with the multi-command alias.
*
* @return commands associated with alias
*/
public Command[] getCommands() {
return commands;
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
boolean result = false;
for (Command command : commands) {
result |= command.execute(sender, commandLabel, args);
}
return result;
}
}

View File

@ -0,0 +1,171 @@
package org.bukkit.command;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.plugin.Plugin;
import java.util.List;
/**
* Represents a {@link Command} belonging to a plugin
*/
public final class PluginCommand extends Command implements PluginIdentifiableCommand {
private final Plugin owningPlugin;
private CommandExecutor executor;
private TabCompleter completer;
protected PluginCommand(String name, Plugin owner) {
super(name);
this.executor = owner;
this.owningPlugin = owner;
this.usageMessage = "";
}
/**
* Executes the command, returning its success
*
* @param sender Source object which is executing this command
* @param commandLabel The alias of the command used
* @param args All arguments passed to the command, split via ' '
* @return true if the command was successful, otherwise false
*/
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
boolean success = false;
if (!owningPlugin.isEnabled()) {
return false;
}
if (!testPermission(sender)) {
return true;
}
try {
success = executor.onCommand(sender, this, commandLabel, args);
} catch (Throwable ex) {
throw new CommandException("Unhandled exception executing command '" + commandLabel + "' in plugin " + owningPlugin.getDescription().getFullName(), ex);
}
if (!success && usageMessage.length() > 0) {
for (String line : usageMessage.replace("<command>", commandLabel).split("\n")) {
sender.sendMessage(line);
}
}
return success;
}
/**
* Sets the {@link CommandExecutor} to run when parsing this command
*
* @param executor New executor to run
*/
public void setExecutor(CommandExecutor executor) {
this.executor = executor == null ? owningPlugin : executor;
}
/**
* Gets the {@link CommandExecutor} associated with this command
*
* @return CommandExecutor object linked to this command
*/
public CommandExecutor getExecutor() {
return executor;
}
/**
* Sets the {@link TabCompleter} to run when tab-completing this command.
* <p>
* If no TabCompleter is specified, and the command's executor implements
* TabCompleter, then the executor will be used for tab completion.
*
* @param completer New tab completer
*/
public void setTabCompleter(TabCompleter completer) {
this.completer = completer;
}
/**
* Gets the {@link TabCompleter} associated with this command.
*
* @return TabCompleter object linked to this command
*/
public TabCompleter getTabCompleter() {
return completer;
}
/**
* Gets the owner of this PluginCommand
*
* @return Plugin that owns this command
*/
public Plugin getPlugin() {
return owningPlugin;
}
/**
* {@inheritDoc}
* <p>
* Delegates to the tab completer if present.
* <p>
* If it is not present or returns null, will delegate to the current
* command executor if it implements {@link TabCompleter}. If a non-null
* list has not been found, will default to standard player name
* completion in {@link
* Command#tabComplete(CommandSender, String, String[])}.
* <p>
* This method does not consider permissions.
*
* @throws CommandException if the completer or executor throw an
* exception during the process of tab-completing.
* @throws IllegalArgumentException if sender, alias, or args is null
*/
@Override
public java.util.List<String> tabComplete(CommandSender sender, String alias, String[] args) throws CommandException, IllegalArgumentException {
return tabComplete(sender, alias, args, null); // PaperSpigot - The code from this method has been (slightly modified) moved to the Location method.
}
// PaperSpigot start - location tab-completes
/**
* This code was copied from tabComplete(CommandSender sender, String alias, String[] args)
*/
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws CommandException, IllegalArgumentException {
Validate.notNull(sender, "Sender cannot be null");
Validate.notNull(args, "Arguments cannot be null");
Validate.notNull(alias, "Alias cannot be null");
List<String> completions = null;
try {
if (completer != null) {
completions = completer.onTabComplete(sender, this, alias, args, location); // PaperSpigot - add location argument
}
if (completions == null && executor instanceof TabCompleter) {
completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args, location); // PaperSpigot - add location argument
}
} catch (Throwable ex) {
StringBuilder message = new StringBuilder();
message.append("Unhandled exception during tab completion for command '/").append(alias).append(' ');
for (String arg : args) {
message.append(arg).append(' ');
}
message.deleteCharAt(message.length() - 1).append("' in plugin ").append(owningPlugin.getDescription().getFullName());
throw new CommandException(message.toString(), ex);
}
if (completions == null) {
return super.tabComplete(sender, alias, args);
}
return completions;
}
// PaperSpigot end
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder(super.toString());
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
stringBuilder.append(", ").append(owningPlugin.getDescription().getFullName()).append(')');
return stringBuilder.toString();
}
}

View File

@ -0,0 +1,76 @@
package org.bukkit.command;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
public class PluginCommandYamlParser {
public static List<Command> parse(Plugin plugin) {
List<Command> pluginCmds = new ArrayList<Command>();
Map<String, Map<String, Object>> map = plugin.getDescription().getCommands();
if (map == null) {
return pluginCmds;
}
for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
if (entry.getKey().contains(":")) {
Bukkit.getServer().getLogger().severe("Could not load command " + entry.getKey() + " for plugin " + plugin.getName() + ": Illegal Characters");
continue;
}
Command newCmd = new PluginCommand(entry.getKey(), plugin);
Object description = entry.getValue().get("description");
Object usage = entry.getValue().get("usage");
Object aliases = entry.getValue().get("aliases");
Object permission = entry.getValue().get("permission");
Object permissionMessage = entry.getValue().get("permission-message");
if (description != null) {
newCmd.setDescription(description.toString());
}
if (usage != null) {
newCmd.setUsage(usage.toString());
}
if (aliases != null) {
List<String> aliasList = new ArrayList<String>();
if (aliases instanceof List) {
for (Object o : (List<?>) aliases) {
if (o.toString().contains(":")) {
Bukkit.getServer().getLogger().severe("Could not load alias " + o.toString() + " for plugin " + plugin.getName() + ": Illegal Characters");
continue;
}
aliasList.add(o.toString());
}
} else {
if (aliases.toString().contains(":")) {
Bukkit.getServer().getLogger().severe("Could not load alias " + aliases.toString() + " for plugin " + plugin.getName() + ": Illegal Characters");
} else {
aliasList.add(aliases.toString());
}
}
newCmd.setAliases(aliasList);
}
if (permission != null) {
newCmd.setPermission(permission.toString());
}
if (permissionMessage != null) {
newCmd.setPermissionMessage(permissionMessage.toString());
}
pluginCmds.add(newCmd);
}
return pluginCmds;
}
}

Some files were not shown because too many files have changed in this diff Show More